diff --git a/notes/剑指 offer 题解.md b/notes/剑指 offer 题解.md index 472b720d..aa6ac568 100644 --- a/notes/剑指 offer 题解.md +++ b/notes/剑指 offer 题解.md @@ -1,90 +1,84 @@ * [前言](#前言) - * [变量命名规范](#变量命名规范) - * [复杂度简写说明](#复杂度简写说明) -* [面试需要的基础知识](#面试需要的基础知识) - * [2. 实现 Singleton](#2-实现-singleton) - * [3. 数组中重复的数字](#3-数组中重复的数字) - * [4. 二维数组中的查找](#4-二维数组中的查找) - * [5. 替换空格](#5-替换空格) - * [6. 从尾到头打印链表](#6-从尾到头打印链表) - * [7. 重建二叉树](#7-重建二叉树) - * [8. 二叉树的下一个结点](#8-二叉树的下一个结点) - * [9. 用两个栈实现队列](#9-用两个栈实现队列) - * [10.1 斐波那契数列](#101-斐波那契数列) - * [10.2 跳台阶](#102-跳台阶) - * [10.3 变态跳台阶](#103-变态跳台阶) - * [10.4 矩形覆盖](#104-矩形覆盖) - * [11. 旋转数组的最小数字](#11-旋转数组的最小数字) - * [12. 矩阵中的路径](#12-矩阵中的路径) - * [13. 机器人的运动范围](#13-机器人的运动范围) - * [14. 剪绳子](#14-剪绳子) - * [15. 二进制中 1 的个数](#15-二进制中-1-的个数) +* [2. 实现 Singleton](#2-实现-singleton) +* [3. 数组中重复的数字](#3-数组中重复的数字) +* [4. 二维数组中的查找](#4-二维数组中的查找) +* [5. 替换空格](#5-替换空格) +* [6. 从尾到头打印链表](#6-从尾到头打印链表) +* [7. 重建二叉树](#7-重建二叉树) +* [8. 二叉树的下一个结点](#8-二叉树的下一个结点) +* [9. 用两个栈实现队列](#9-用两个栈实现队列) +* [10.1 斐波那契数列](#101-斐波那契数列) +* [10.2 跳台阶](#102-跳台阶) +* [10.3 变态跳台阶](#103-变态跳台阶) +* [10.4 矩形覆盖](#104-矩形覆盖) +* [11. 旋转数组的最小数字](#11-旋转数组的最小数字) +* [12. 矩阵中的路径](#12-矩阵中的路径) +* [13. 机器人的运动范围](#13-机器人的运动范围) +* [14. 剪绳子](#14-剪绳子) +* [15. 二进制中 1 的个数](#15-二进制中-1-的个数) * [高质量的代码](#高质量的代码) - * [16. 数值的整数次方](#16-数值的整数次方) - * [17. 打印从 1 到最大的 n 位数](#17-打印从-1-到最大的-n-位数) - * [18.1 在 O(1) 时间内删除链表节点](#181-在-o1-时间内删除链表节点) - * [18.2 删除链表中重复的结点](#182-删除链表中重复的结点) - * [19. 正则表达式匹配](#19-正则表达式匹配) - * [20. 表示数值的字符串](#20-表示数值的字符串) - * [21. 调整数组顺序使奇数位于偶数前面](#21-调整数组顺序使奇数位于偶数前面) - * [22. 链表中倒数第 K 个结点](#22-链表中倒数第-k-个结点) - * [23. 链表中环的入口结点](#23-链表中环的入口结点) - * [24. 反转链表](#24-反转链表) - * [25. 合并两个排序的链表](#25-合并两个排序的链表) - * [26. 树的子结构](#26-树的子结构) +* [16. 数值的整数次方](#16-数值的整数次方) +* [17. 打印从 1 到最大的 n 位数](#17-打印从-1-到最大的-n-位数) +* [18.1 在 O(1) 时间内删除链表节点](#181-在-o1-时间内删除链表节点) +* [18.2 删除链表中重复的结点](#182-删除链表中重复的结点) +* [19. 正则表达式匹配](#19-正则表达式匹配) +* [20. 表示数值的字符串](#20-表示数值的字符串) +* [21. 调整数组顺序使奇数位于偶数前面](#21-调整数组顺序使奇数位于偶数前面) +* [22. 链表中倒数第 K 个结点](#22-链表中倒数第-k-个结点) +* [23. 链表中环的入口结点](#23-链表中环的入口结点) +* [24. 反转链表](#24-反转链表) +* [25. 合并两个排序的链表](#25-合并两个排序的链表) +* [26. 树的子结构](#26-树的子结构) * [解决面试题的思路](#解决面试题的思路) - * [27. 二叉树的镜像](#27-二叉树的镜像) - * [28.1 对称的二叉树](#281-对称的二叉树) - * [28.2 平衡二叉树](#282-平衡二叉树) - * [29. 顺时针打印矩阵](#29-顺时针打印矩阵) - * [30. 包含 min 函数的栈](#30-包含-min-函数的栈) - * [31. 栈的压入、弹出序列](#31-栈的压入弹出序列) - * [32.1 从上往下打印二叉树](#321-从上往下打印二叉树) - * [32.2 把二叉树打印成多行](#322-把二叉树打印成多行) - * [32.3 按之字形顺序打印二叉树](#323-按之字形顺序打印二叉树) - * [33. 二叉搜索树的后序遍历序列](#33-二叉搜索树的后序遍历序列) - * [34. 二叉树中和为某一值的路径](#34-二叉树中和为某一值的路径) - * [35. 复杂链表的复制](#35-复杂链表的复制) - * [36. 二叉搜索树与双向链表](#36-二叉搜索树与双向链表) - * [37. 序列化二叉树](#37-序列化二叉树) - * [38. 字符串的排列](#38-字符串的排列) +* [27. 二叉树的镜像](#27-二叉树的镜像) +* [28.1 对称的二叉树](#281-对称的二叉树) +* [28.2 平衡二叉树](#282-平衡二叉树) +* [29. 顺时针打印矩阵](#29-顺时针打印矩阵) +* [30. 包含 min 函数的栈](#30-包含-min-函数的栈) +* [31. 栈的压入、弹出序列](#31-栈的压入弹出序列) +* [32.1 从上往下打印二叉树](#321-从上往下打印二叉树) +* [32.2 把二叉树打印成多行](#322-把二叉树打印成多行) +* [32.3 按之字形顺序打印二叉树](#323-按之字形顺序打印二叉树) +* [33. 二叉搜索树的后序遍历序列](#33-二叉搜索树的后序遍历序列) +* [34. 二叉树中和为某一值的路径](#34-二叉树中和为某一值的路径) +* [35. 复杂链表的复制](#35-复杂链表的复制) +* [36. 二叉搜索树与双向链表](#36-二叉搜索树与双向链表) +* [37. 序列化二叉树](#37-序列化二叉树) +* [38. 字符串的排列](#38-字符串的排列) * [优化时间和空间效率](#优化时间和空间效率) - * [39. 数组中出现次数超过一半的数字](#39-数组中出现次数超过一半的数字) - * [40. 最小的 K 个数](#40-最小的-k-个数) - * [41.1 数据流中的中位数](#411-数据流中的中位数) - * [41.2 字符流中第一个不重复的字符](#412-字符流中第一个不重复的字符) - * [42. 连续子数组的最大和](#42-连续子数组的最大和) - * [43. 从 1 到 n 整数中 1 出现的次数](#43-从-1-到-n-整数中-1-出现的次数) - * [44. 数字序列中的某一位数字](#44-数字序列中的某一位数字) - * [45. 把数组排成最小的数](#45-把数组排成最小的数) - * [46. 把数字翻译成字符串](#46-把数字翻译成字符串) - * [47. 礼物的最大价值](#47-礼物的最大价值) - * [48. 最长不含重复字符的子字符串](#48-最长不含重复字符的子字符串) - * [49. 丑数](#49-丑数) - * [50. 第一个只出现一次的字符位置](#50-第一个只出现一次的字符位置) - * [51. 数组中的逆序对](#51-数组中的逆序对) - * [52. 两个链表的第一个公共结点](#52-两个链表的第一个公共结点) +* [39. 数组中出现次数超过一半的数字](#39-数组中出现次数超过一半的数字) +* [40. 最小的 K 个数](#40-最小的-k-个数) +* [41.1 数据流中的中位数](#411-数据流中的中位数) +* [41.2 字符流中第一个不重复的字符](#412-字符流中第一个不重复的字符) +* [42. 连续子数组的最大和](#42-连续子数组的最大和) +* [43. 从 1 到 n 整数中 1 出现的次数](#43-从-1-到-n-整数中-1-出现的次数) +* [44. 数字序列中的某一位数字](#44-数字序列中的某一位数字) +* [45. 把数组排成最小的数](#45-把数组排成最小的数) +* [46. 把数字翻译成字符串](#46-把数字翻译成字符串) +* [47. 礼物的最大价值](#47-礼物的最大价值) +* [解题思路](#解题思路) +* [48. 最长不含重复字符的子字符串](#48-最长不含重复字符的子字符串) +* [50. 第一个只出现一次的字符位置](#50-第一个只出现一次的字符位置) +* [52. 两个链表的第一个公共结点](#52-两个链表的第一个公共结点) * [面试中的各项能力](#面试中的各项能力) - * [53 数字在排序数组中出现的次数](#53-数字在排序数组中出现的次数) - * [54. 二叉搜索树的第 k 个结点](#54-二叉搜索树的第-k-个结点) - * [55 二叉树的深度](#55-二叉树的深度) - * [56. 数组中只出现一次的数字](#56-数组中只出现一次的数字) - * [57.1 和为 S 的两个数字](#571-和为-s-的两个数字) - * [57.2 和为 S 的连续正数序列](#572-和为-s-的连续正数序列) - * [58.1 翻转单词顺序列](#581-翻转单词顺序列) - * [58.2 左旋转字符串](#582-左旋转字符串) - * [59. 滑动窗口的最大值](#59-滑动窗口的最大值) - * [60. n 个骰子的点数](#60-n-个骰子的点数) - * [61. 扑克牌顺子](#61-扑克牌顺子) - * [62. 圆圈中最后剩下的数](#62-圆圈中最后剩下的数) - * [63. 股票的最大利润](#63-股票的最大利润) - * [64. 求 1+2+3+...+n](#64-求-1+2+3++n) - * [65. 不用加减乘除做加法](#65-不用加减乘除做加法) - * [66. 构建乘积数组](#66-构建乘积数组) +* [53 数字在排序数组中出现的次数](#53-数字在排序数组中出现的次数) +* [54. 二叉搜索树的第 k 个结点](#54-二叉搜索树的第-k-个结点) +* [55 二叉树的深度](#55-二叉树的深度) +* [57.1 和为 S 的两个数字](#571-和为-s-的两个数字) +* [57.2 和为 S 的连续正数序列](#572-和为-s-的连续正数序列) +* [58.1 翻转单词顺序列](#581-翻转单词顺序列) +* [58.2 左旋转字符串](#582-左旋转字符串) +* [59. 滑动窗口的最大值](#59-滑动窗口的最大值) +* [60. n 个骰子的点数](#60-n-个骰子的点数) +* [61. 扑克牌顺子](#61-扑克牌顺子) +* [62. 圆圈中最后剩下的数](#62-圆圈中最后剩下的数) +* [63. 股票的最大利润](#63-股票的最大利润) +* [64. 求 1+2+3+...+n](#64-求-1+2+3++n) +* [66. 构建乘积数组](#66-构建乘积数组) * [两个面试案例](#两个面试案例) - * [67. 把字符串转换成整数](#67-把字符串转换成整数) - * [68. 树中两个节点的最低公共祖先](#68-树中两个节点的最低公共祖先) +* [67. 把字符串转换成整数](#67-把字符串转换成整数) +* [68. 树中两个节点的最低公共祖先](#68-树中两个节点的最低公共祖先) @@ -105,19 +99,17 @@ O(nlogn) + O(n2),第一个指时间复杂度,第二个指空间复杂度。 -# 面试需要的基础知识 - -## 2. 实现 Singleton +# 2. 实现 Singleton [单例模式](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F) -## 3. 数组中重复的数字 +# 3. 数组中重复的数字 -### 题目描述 +## 题目描述 在一个长度为 n 的数组里的所有数字都在 0 到 n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。例如,如果输入长度为 7 的数组 {2, 3, 1, 0, 2, 5, 3},那么对应的输出是第一个重复的数字 2。 -### 解题思路 +## 解题思路 这种数组元素在 [0, n-1] 范围内的问题,可以将值为 i 的元素放到第 i 个位置上。 @@ -156,9 +148,9 @@ private void swap(int[] nums, int i, int j) { } ``` -## 4. 二维数组中的查找 +# 4. 二维数组中的查找 -### 题目描述 +## 题目描述 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 @@ -176,7 +168,7 @@ Given target = 5, return true. Given target = 20, return false. ``` -### 解题思路 +## 解题思路 从右上角开始查找。因为矩阵中的一个数,它左边的数都比它来的小,下边的数都比它来的大。因此,从右上角开始查找,就可以根据 target 和当前元素的大小关系来改变行和列的下标,从而缩小查找区间。 @@ -196,13 +188,13 @@ public boolean Find(int target, int[][] matrix) { } ``` -## 5. 替换空格 +# 5. 替换空格 -### 题目描述 +## 题目描述 请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为 We Are Happy. 则经过替换之后的字符串为 We%20Are%20Happy。 -### 解题思路 +## 解题思路 现在字符串尾部填充任意字符,使得字符串的长度等于将字符串替换之后的长度。因为一个空格要替换成三个字符(%20),因此当遍历到一个空格时,需要在尾部填充两个任意字符。 @@ -238,15 +230,15 @@ public String replaceSpace(StringBuffer str) { } ``` -## 6. 从尾到头打印链表 +# 6. 从尾到头打印链表 -### 题目描述 +## 题目描述 输入链表的第一个节点,从尾到头反过来打印出每个结点的值。 -### 解题思路 +## 解题思路 -栈 +### 使用栈 ```java public ArrayList printListFromTailToHead(ListNode listNode) { @@ -263,7 +255,7 @@ public ArrayList printListFromTailToHead(ListNode listNode) { } ``` -递归 +### 使用递归 ```java public ArrayList printListFromTailToHead(ListNode listNode) { @@ -276,7 +268,7 @@ public ArrayList printListFromTailToHead(ListNode listNode) { } ``` -正向遍历然后调用 Collections.reverse()。 +### 使用 Collections.reverse() ```java public ArrayList printListFromTailToHead(ListNode listNode) { @@ -290,7 +282,9 @@ public ArrayList printListFromTailToHead(ListNode listNode) { } ``` -不使用库函数,并且不使用递归。利用链表头插法为逆序的特点。 +### 使用头插法 + +利用链表头插法为逆序的特点。 头结点和第一个节点的区别:头结点是在头插法中使用的一个额外节点,这个节点不存储值;第一个节点就是链表的第一个真正存储值的节点。 @@ -315,9 +309,9 @@ public ArrayList printListFromTailToHead(ListNode listNode) { } ``` -## 7. 重建二叉树 +# 7. 重建二叉树 -### 题目描述 +## 题目描述 根据二叉树的前序遍历和中序遍历的结果,重建出该二叉树。 @@ -336,7 +330,7 @@ Return the following binary tree: 15 7 ``` -### 解题思路 +## 解题思路 前序遍历的第一个值为树根节点的值,使用这个值将中序遍历结果分成两部分,左部分为树的左子树中序遍历结果,右部分为树的右子树中序遍历的结果。 @@ -362,19 +356,19 @@ private TreeNode reConstructBinaryTree(int[] pre, int preL, int preR, int[] in, } ``` -## 8. 二叉树的下一个结点 +# 8. 二叉树的下一个结点 -### 题目描述 +## 题目描述 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。 -### 解题思路 +## 解题思路 -- 如果一个节点有右子树不为空,那么该节点的下一个节点是右子树的最左节点; +① 如果一个节点有右子树不为空,那么该节点的下一个节点是右子树的最左节点;

-- 否则,向上找第一个左链接指向的树包含该节点的祖先节点。 +② 否则,向上找第一个左链接指向的树包含该节点的祖先节点。

@@ -408,9 +402,9 @@ public TreeLinkNode GetNext(TreeLinkNode pNode) { } ``` -## 9. 用两个栈实现队列 +# 9. 用两个栈实现队列 -### 解题思路 +## 解题思路 in 栈用来处理入栈(push)操作,out 栈用来处理出栈(pop)操作。一个元素进入 in 栈之后,出栈的顺序被反转。当元素要出栈时,需要先进入 out 栈,此时元素出栈顺序再一次被反转,因此出栈顺序就和最开始入栈顺序是相同的,此时先进入的元素先退出,这就是队列的顺序。 @@ -432,15 +426,15 @@ public int pop() { } ``` -## 10.1 斐波那契数列 +# 10.1 斐波那契数列 -### 题目描述 +## 题目描述 以 O(1) 的时间复杂度求菲波那切数列。

-### 解题思路 +## 解题思路 如果使用递归求解,那么会重复计算一些子问题。例如,求 f(10) 需要计算 f(9) 和 f(8),计算 f(9) 需要计算 f(8) 和 f(7),可以看到 f(8) 被重复计算了。 @@ -464,9 +458,9 @@ public class Solution { } ``` -## 10.2 跳台阶 +# 10.2 跳台阶 -### 题目描述 +## 题目描述 一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 @@ -483,9 +477,9 @@ public int JumpFloor(int n) { } ``` -## 10.3 变态跳台阶 +# 10.3 变态跳台阶 -### 题目描述 +## 题目描述 一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级……它也可以跳上 n 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 @@ -502,9 +496,9 @@ public int JumpFloorII(int n) { } ``` -## 10.4 矩形覆盖 +# 10.4 矩形覆盖 -### 题目描述 +## 题目描述 我们可以用 2\*1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2\*1 的小矩形无重叠地覆盖一个 2\*n 的大矩形,总共有多少种方法? @@ -521,15 +515,15 @@ public int RectCover(int n) { } ``` -## 11. 旋转数组的最小数字 +# 11. 旋转数组的最小数字 -### 题目描述 +## 题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组 {3, 4, 5, 1, 2} 为 {1, 2, 3, 4, 5} 的一个旋转,该数组的最小值为 1。NOTE:给出的所有元素都大于 0,若数组大小为 0,请返回 0。 -### 解题思路 +## 解题思路 -分治 +#### 分治 复杂度:O(logn) + O(1),其实空间复杂度不止 O(1),因为分治使用了递归栈,用到了额外的空间,如果对空间有要求就不能用这种方法。 @@ -546,7 +540,7 @@ private int minNumberInRotateArray(int[] nums, int first, int last) { } ``` -双指针 +#### 双指针 复杂度:O(logn) + O(1) @@ -565,12 +559,14 @@ public int minNumberInRotateArray(int[] array) { } ``` -## 12. 矩阵中的路径 +# 12. 矩阵中的路径 -### 题目描述 +## 题目描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。例如 a b c e s f c s a d e e 矩阵中包含一条字符串 "bcced" 的路径,但是矩阵中不包含 "abcb" 路径,因为字符串的第一个字符 b 占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。 +## 解题思路 + ```java private int[][] next = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}}; private int rows; @@ -610,12 +606,14 @@ private boolean backtracking(char[][] matrix, char[] str, boolean[][] used, int } ``` -## 13. 机器人的运动范围 +# 13. 机器人的运动范围 -### 题目描述 +## 题目描述 地上有一个 m 行和 n 列的方格。一个机器人从坐标 (0, 0) 的格子开始移动,每一次只能向左右上下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于 k 的格子。例如,当 k 为 18 时,机器人能够进入方格(35, 37),因为 3+5+3+7=18。但是,它不能进入方格(35, 38),因为 3+5+3+8=19。请问该机器人能够达到多少个格子? +## 解题思路 + ```java private int cnt = 0; private int[][] next = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}}; @@ -656,17 +654,19 @@ private void initDigitSum(int rows, int cols) { } ``` -## 14. 剪绳子 +# 14. 剪绳子 -### 题目描述 +## 题目描述 把一根绳子剪成多段,并且使得每段的长度乘积最大。 -**动态规划解法** +## 解题思路 + +#### 1. 动态规划解法 [分割整数](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3.md#%E5%88%86%E5%89%B2%E6%95%B4%E6%95%B0) -**贪心解法** +#### 2. 贪心解法 尽可能多得剪长度为 3 的绳子,并且不允许有长度为 1 的绳子出现,如果出现了,就从已经切好长度为 3 的绳子中拿出一段与长度为 1 的绳子重新组合,把它们切成两段长度为 2 的绳子。 @@ -682,9 +682,9 @@ int maxProductAfterCuttin(int length) { } ``` -## 15. 二进制中 1 的个数 +# 15. 二进制中 1 的个数 -### 题目描述 +## 题目描述 输入一个整数,输出该数二进制表示中 1 的个数。其中负数用补码表示 @@ -713,13 +713,13 @@ public int NumberOf1(int n) { # 高质量的代码 -## 16. 数值的整数次方 +# 16. 数值的整数次方 -### 题目描述 +## 题目描述 给定一个 double 类型的浮点数 base 和 int 类型的整数 exponent。求 base 的 exponent 次方。 -### 解题思路 +## 解题思路 下面的讨论中 x 代表 base,N 代表 exponent。 @@ -743,13 +743,13 @@ public double Power(double base, int exponent) { } ``` -## 17. 打印从 1 到最大的 n 位数 +# 17. 打印从 1 到最大的 n 位数 -### 题目描述 +## 题目描述 输入数字 n,按顺序打印出从 1 最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数即 999。 -### 解题思路 +## 解题思路 由于 n 可能会非常大,因此不能直接用 int 存储数,而是用 char 数组进行存储。 @@ -783,9 +783,9 @@ private void printNumber(char[] number) { } ``` -## 18.1 在 O(1) 时间内删除链表节点 +# 18.1 在 O(1) 时间内删除链表节点 -### 解题思路 +## 解题思路 - 如果链表不是尾节点,那么可以直接将下一个节点的值赋给节点,令节点指向下下个节点,然后删除下一个节点,时间复杂度为 O(1)。 @@ -815,9 +815,9 @@ public ListNode deleteNode(ListNode head, ListNode tobeDelete) { } ``` -## 18.2 删除链表中重复的结点 +# 18.2 删除链表中重复的结点 -### 题目描述 +## 题目描述 ```html Input : 1->2->3->3->4->4->5 @@ -845,9 +845,9 @@ public ListNode deleteDuplication(ListNode pHead) { } ``` -## 19. 正则表达式匹配 +# 19. 正则表达式匹配 -### 题目描述 +## 题目描述 请实现一个函数用来匹配包括 '.' 和 '\*' 的正则表达式。模式中的字符 '.' 表示任意一个字符,而 '\*' 表示它前面的字符可以出现任意次(包含 0 次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串 "aaa" 与模式 "a.a" 和 "ab\*ac\*a" 匹配,但是与 "aa.a" 和 "ab\*a" 均不匹配. @@ -878,9 +878,9 @@ public boolean match(char[] s, char[] p) { } ``` -## 20. 表示数值的字符串 +# 20. 表示数值的字符串 -### 题目描述 +## 题目描述 请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串 "+100","5e2","-123","3.1416" 和 "-1E-16" 都表示数值。 但是 "12e","1a3.14","1.2.3","+-5" 和 "12e+4.3" 都不是。 @@ -891,13 +891,13 @@ public boolean isNumeric(char[] str) { } ``` -## 21. 调整数组顺序使奇数位于偶数前面 +# 21. 调整数组顺序使奇数位于偶数前面 **题目要求** 保证奇数和奇数,偶数和偶数之间的相对位置不变,这和书本不太一样。 -### 解题思路 +## 解题思路 - 时间复杂度 : O(n2) - 空间复杂度 : O(1) @@ -936,9 +936,9 @@ public void reOrderArray(int[] array) { } ``` -## 22. 链表中倒数第 K 个结点 +# 22. 链表中倒数第 K 个结点 -### 解题思路 +## 解题思路 设链表的长度为 N。设两个指针 P1 和 P2,先让 P1 移动 K 个节点,则还有 N - K 个节点可以移动。此时让 P1 和 P2 同时移动,可以知道当 P1 移动到链表结尾时,P2 移动到 N - K 个节点处,该位置就是倒数第 K 个节点。 @@ -959,9 +959,9 @@ public ListNode FindKthToTail(ListNode head, int k) { } ``` -## 23. 链表中环的入口结点 +# 23. 链表中环的入口结点 -### 解题思路 +## 解题思路 使用双指针,一个指针 fast 每次移动两个节点,一个指针 slow 每次移动一个节点。因为存在环,所以两个指针必定相遇在环中的某个节点上。此时 fast 移动的节点数为 x+2y+z,slow 为 x+y,由于 fast 速度比 slow 快一倍,因此 x+2y+z=2(x+y),得到 x=z。 @@ -989,9 +989,9 @@ public ListNode EntryNodeOfLoop(ListNode pHead) { } ``` -## 24. 反转链表 +# 24. 反转链表 -### 解题思路 +## 解题思路 头插法 @@ -1008,9 +1008,9 @@ public ListNode ReverseList(ListNode head) { } ``` -## 25. 合并两个排序的链表 +# 25. 合并两个排序的链表 -### 题目描述 +## 题目描述

@@ -1034,9 +1034,9 @@ public ListNode Merge(ListNode list1, ListNode list2) { } ``` -## 26. 树的子结构 +# 26. 树的子结构 -### 题目描述 +## 题目描述

@@ -1057,9 +1057,9 @@ private boolean isSubtree(TreeNode root1, TreeNode root2) { # 解决面试题的思路 -## 27. 二叉树的镜像 +# 27. 二叉树的镜像 -### 题目描述 +## 题目描述

@@ -1074,9 +1074,9 @@ public void Mirror(TreeNode root) { } ``` -## 28.1 对称的二叉树 +# 28.1 对称的二叉树 -### 题目描述 +## 题目描述 ```html 1 @@ -1100,9 +1100,9 @@ boolean isSymmetrical(TreeNode t1, TreeNode t2) { } ``` -## 28.2 平衡二叉树 +# 28.2 平衡二叉树 -### 题目描述 +## 题目描述 ```html 3 @@ -1131,9 +1131,9 @@ private int height(TreeNode root) { } ``` -## 29. 顺时针打印矩阵 +# 29. 顺时针打印矩阵 -### 题目描述 +## 题目描述 下图的矩阵顺时针打印结果为:1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10 @@ -1154,9 +1154,9 @@ public ArrayList printMatrix(int[][] matrix) { } ``` -## 30. 包含 min 函数的栈 +# 30. 包含 min 函数的栈 -### 题目描述 +## 题目描述 定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的 min 函数。 @@ -1186,9 +1186,9 @@ public int min() { } ``` -## 31. 栈的压入、弹出序列 +# 31. 栈的压入、弹出序列 -### 题目描述 +## 题目描述 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列 1,2,3,4,5 是某栈的压入顺序,序列 4,5,3,2,1 是该压栈序列对应的一个弹出序列,但 4,3,5,1,2 就不可能是该压栈序列的弹出序列。 @@ -1207,9 +1207,9 @@ public boolean IsPopOrder(int[] pushA, int[] popA) { } ``` -## 32.1 从上往下打印二叉树 +# 32.1 从上往下打印二叉树 -### 题目描述 +## 题目描述 从上往下打印出二叉树的每个节点,同层节点从左至右打印。 @@ -1217,7 +1217,7 @@ public boolean IsPopOrder(int[] pushA, int[] popA) {

-### 解题思路 +## 解题思路 使用队列来进行层次遍历。 @@ -1242,9 +1242,9 @@ public ArrayList PrintFromTopToBottom(TreeNode root) { } ``` -## 32.2 把二叉树打印成多行 +# 32.2 把二叉树打印成多行 -### 题目描述 +## 题目描述 和上题几乎一样。 @@ -1269,9 +1269,9 @@ ArrayList> Print(TreeNode pRoot) { } ``` -## 32.3 按之字形顺序打印二叉树 +# 32.3 按之字形顺序打印二叉树 -### 题目描述 +## 题目描述 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。 @@ -1303,9 +1303,9 @@ public ArrayList> Print(TreeNode pRoot) { } ``` -## 33. 二叉搜索树的后序遍历序列 +# 33. 二叉搜索树的后序遍历序列 -### 题目描述 +## 题目描述 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。 @@ -1331,9 +1331,9 @@ private boolean verify(int[] sequence, int first, int last) { } ``` -## 34. 二叉树中和为某一值的路径 +# 34. 二叉树中和为某一值的路径 -### 题目描述 +## 题目描述 输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。 @@ -1363,15 +1363,15 @@ private void dfs(TreeNode node, int target, int curSum, ArrayList path) } ``` -## 35. 复杂链表的复制 +# 35. 复杂链表的复制 -### 题目描述 +## 题目描述 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

-### 解题思路 +## 解题思路 第一步,在每个节点的后面插入复制的节点。 @@ -1419,9 +1419,9 @@ public RandomListNode Clone(RandomListNode pHead) { } ``` -## 36. 二叉搜索树与双向链表 +# 36. 二叉搜索树与双向链表 -### 题目描述 +## 题目描述 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。 @@ -1446,9 +1446,9 @@ private void inOrder(TreeNode node) { } ``` -## 37. 序列化二叉树 +# 37. 序列化二叉树 -### 题目描述 +## 题目描述 请实现两个函数,分别用来序列化和反序列化二叉树。 @@ -1482,9 +1482,9 @@ public class Solution { } ``` -## 38. 字符串的排列 +# 38. 字符串的排列 -### 题目描述 +## 题目描述 输入一个字符串 , 按字典序打印出该字符串中字符的所有排列。例如输入字符串 abc, 则打印出由字符 a, b, c 所能排列出来的所有字符串 abc, acb, bac, bca, cab 和 cba。 @@ -1518,9 +1518,9 @@ private void backtracking(char[] chars, boolean[] hasUsed, StringBuffer s) { # 优化时间和空间效率 -## 39. 数组中出现次数超过一半的数字 +# 39. 数组中出现次数超过一半的数字 -### 解题思路 +## 解题思路 多数投票问题,可以利用 Boyer-Moore Majority Vote Algorithm 来解决这个问题,使得时间复杂度为 O(n)。 @@ -1545,7 +1545,7 @@ public int MoreThanHalfNum_Solution(int[] nums) { } ``` -## 40. 最小的 K 个数 +# 40. 最小的 K 个数 **快速选择** @@ -1635,9 +1635,9 @@ public ArrayList GetLeastNumbers_Solution(int[] input, int k) { } ``` -## 41.1 数据流中的中位数 +# 41.1 数据流中的中位数 -### 题目描述 +## 题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。 @@ -1675,9 +1675,9 @@ public class Solution { } ``` -## 41.2 字符流中第一个不重复的字符 +# 41.2 字符流中第一个不重复的字符 -### 题目描述 +## 题目描述 请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符 "go" 时,第一个只出现一次的字符是 "g"。当从该字符流中读出前六个字符“google" 时,第一个只出现一次的字符是 "l"。 @@ -1701,9 +1701,9 @@ public class Solution { } ``` -## 42. 连续子数组的最大和 +# 42. 连续子数组的最大和 -### 题目描述 +## 题目描述 {6,-3,-2,7,-15,1,2,2},连续子向量的最大和为 8(从第 0 个开始,到第 3 个为止) @@ -1721,7 +1721,7 @@ public int FindGreatestSumOfSubArray(int[] nums) { } ``` -## 43. 从 1 到 n 整数中 1 出现的次数 +# 43. 从 1 到 n 整数中 1 出现的次数 解题参考:[Leetcode : 233. Number of Digit One](https://leetcode.com/problems/number-of-digit-one/discuss/64381/4+-lines-O(log-n)-C++JavaPython) @@ -1736,9 +1736,9 @@ public int NumberOf1Between1AndN_Solution(int n) { } ``` -## 44. 数字序列中的某一位数字 +# 44. 数字序列中的某一位数字 -### 题目描述 +## 题目描述 数字以 0123456789101112131415... 的格式序列化到一个字符串中,求这个字符串的第 index 位。 @@ -1785,13 +1785,13 @@ private int beginNumber(int digit) { } ``` -## 45. 把数组排成最小的数 +# 45. 把数组排成最小的数 -### 题目描述 +## 题目描述 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组 {3,32,321},则打印出这三个数字能排成的最小数字为 321323。 -### 解题思路 +## 解题思路 可以看成是一个排序问题,在比较两个字符串 S1 和 S2 的大小时,应该比较的是 S1+S2 和 S2+S1 的大小,如果 S1+S2 >= S2+S1,那么应该把 S1 排在前面,否则应该把 S2 排在前面。 @@ -1807,9 +1807,9 @@ public String PrintMinNumber(int[] numbers) { } ``` -## 46. 把数字翻译成字符串 +# 46. 把数字翻译成字符串 -### 题目描述 +## 题目描述 给定一个数字,按照如下规则翻译成字符串:0 翻译成“a”,1 翻译成“b”...25 翻译成“z”。一个数字有多种翻译可能,例如 12258 一共有 5 种,分别是 bccfi,bwfi,bczi,mcfi,mzi。实现一个函数,用来计算一个数字有多少种不同的翻译方法。 @@ -1829,9 +1829,9 @@ public int getTranslationCount(String number) { } ``` -## 47. 礼物的最大价值 +# 47. 礼物的最大价值 -### 题目描述 +## 题目描述 在一个 m\*n 的棋盘的每一个格都放有一个礼物,每个礼物都有一定价值(大于 0)。从左上角开始拿礼物,每次向右或向下移动一格,直到右下角结束。给定一个棋盘,求拿到礼物的最大价值。例如,对于如下棋盘 @@ -1844,7 +1844,7 @@ public int getTranslationCount(String number) { 礼物的最大价值为 1+12+5+7+7+16+5=53。 -### 解题思路 +# 解题思路 应该用动态规划求解,而不是深度优先搜索,深度优先搜索过于复杂,不是最优解。 @@ -1864,9 +1864,9 @@ public int getMaxValue(int[][] values) { } ``` -## 48. 最长不含重复字符的子字符串 +# 48. 最长不含重复字符的子字符串 -### 题目描述 +## 题目描述 输入一个字符串(只包含 a\~z 的字符),求其最长不含重复字符的子字符串的长度。例如对于 arabcacfr,最长不含重复字符的子字符串为 acfr,长度为 4。 @@ -1892,7 +1892,7 @@ public int longestSubStringWithoutDuplication(String str) { ## 49. 丑数 -### 题目描述 +## 题目描述 把只包含因子 2、3 和 5 的数称作丑数(Ugly Number)。例如 6、8 都是丑数,但 14 不是,因为它包含因子 7。 习惯上我们把 1 当做是第一个丑数。求按从小到大的顺序的第 N 个丑数。 @@ -1915,7 +1915,7 @@ public int GetUglyNumber_Solution(int N) { } ``` -## 50. 第一个只出现一次的字符位置 +# 50. 第一个只出现一次的字符位置 ```java public int FirstNotRepeatingChar(String str) { @@ -1928,7 +1928,7 @@ public int FirstNotRepeatingChar(String str) { ## 51. 数组中的逆序对 -### 题目描述 +## 题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数 P。 @@ -1968,9 +1968,9 @@ private void merge(int[] nums, int start, int mid, int end) { } ``` -## 52. 两个链表的第一个公共结点 +# 52. 两个链表的第一个公共结点 -### 题目描述 +## 题目描述 ```html A: a1 → a2 @@ -1980,7 +1980,7 @@ A: a1 → a2 B: b1 → b2 → b3 ``` -### 解题思路 +## 解题思路 设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。 @@ -1999,9 +1999,9 @@ public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) { # 面试中的各项能力 -## 53 数字在排序数组中出现的次数 +# 53 数字在排序数组中出现的次数 -### 题目描述 +## 题目描述 ```html Input: @@ -2011,7 +2011,7 @@ Output: 4 ```` -### 解题思路 +## 解题思路 可以用二分查找找出数字在数组的最左端和最右端。 @@ -2045,9 +2045,9 @@ private int getLastK(int[] nums, int K) { } ``` -## 54. 二叉搜索树的第 k 个结点 +# 54. 二叉搜索树的第 k 个结点 -### 解题思路 +## 解题思路 利用二叉搜索数中序遍历有序的特点。 @@ -2069,7 +2069,7 @@ private void inOrder(TreeNode root, int k) { } ``` -## 55 二叉树的深度 +# 55 二叉树的深度 ```java public int TreeDepth(TreeNode root) { @@ -2080,11 +2080,11 @@ public int TreeDepth(TreeNode root) { ## 56. 数组中只出现一次的数字 -### 题目描述 +## 题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了两次,找出这两个数。 -### 解题思路 +## 解题思路 两个不相等的元素在位级表示上必定会有一位存在不同。 @@ -2105,13 +2105,13 @@ public void FindNumsAppearOnce(int[] array, int num1[], int num2[]) { } ``` -## 57.1 和为 S 的两个数字 +# 57.1 和为 S 的两个数字 -### 题目描述 +## 题目描述 输入一个递增排序的数组和一个数字 S,在数组中查找两个数,是的他们的和正好是 S,如果有多对数字的和等于 S,输出两个数的乘积最小的。 -### 解题思路 +## 解题思路 使用双指针,一个指针指向元素较小的值,一个指针指向元素较大的值。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。 @@ -2130,9 +2130,9 @@ public ArrayList FindNumbersWithSum(int[] array, int sum) { } ``` -## 57.2 和为 S 的连续正数序列 +# 57.2 和为 S 的连续正数序列 -### 题目描述 +## 题目描述 和为 100 的连续序列有 18, 19, 20, 21, 22 @@ -2165,15 +2165,15 @@ public ArrayList> FindContinuousSequence(int sum) { } ``` -## 58.1 翻转单词顺序列 +# 58.1 翻转单词顺序列 -### 题目描述 +## 题目描述 输入:"I am a student." 输出:"student. a am I" -### 解题思路 +## 解题思路 题目应该有一个隐含条件,就是不能用额外的空间。虽然 Java 的题目输入参数为 String 类型,需要先创建一个字符数组使得空间复杂度为 O(n),但是正确的参数类型应该和原书一样,为字符数组,并且只能使用该字符数组的空间。任何使用了额外空间的解法在面试时都会大打折扣,包括递归解法。正确的解法应该是和书上一样,先旋转每个单词,再旋转整个字符串。 @@ -2205,9 +2205,9 @@ private void reverse(char[] c, int start, int end) { } ``` -## 58.2 左旋转字符串 +# 58.2 左旋转字符串 -### 题目描述 +## 题目描述 对于一个给定的字符序列 S,请你把其循环左移 K 位后的序列输出。例如,字符序列 S=”abcXYZdef”, 要求输出循环左移 3 位后的结果,即“XYZdefabc”。 @@ -2232,9 +2232,9 @@ private void reverse(char[] c, int i, int j) { } ``` -## 59. 滑动窗口的最大值 +# 59. 滑动窗口的最大值 -### 题目描述 +## 题目描述 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组 {2, 3, 4, 2, 6, 2, 5, 1} 及滑动窗口的大小 3,那么一共存在 6 个滑动窗口,他们的最大值分别为 {4, 4, 6, 6, 6, 5}; @@ -2255,9 +2255,9 @@ public ArrayList maxInWindows(int[] num, int size) { } ``` -## 60. n 个骰子的点数 +# 60. n 个骰子的点数 -### 题目描述 +## 题目描述 把 n 个骰子仍在地上,求点数和为 s 的概率。 @@ -2315,9 +2315,9 @@ public double countProbability(int n, int s) { } ``` -## 61. 扑克牌顺子 +# 61. 扑克牌顺子 -### 题目描述 +## 题目描述 五张牌,其中大小鬼为癞子,牌面大小为 0。判断是否能组成顺子。 @@ -2337,13 +2337,13 @@ public boolean isContinuous(int [] numbers) { } ``` -## 62. 圆圈中最后剩下的数 +# 62. 圆圈中最后剩下的数 -### 题目描述 +## 题目描述 让小朋友们围成一个大圈。然后 , 他随机指定一个数 m, 让编号为 0 的小朋友开始报数。每次喊到 m-1 的那个小朋友要出列唱首歌 , 然后可以在礼品箱中任意的挑选礼物 , 并且不再回到圈中 , 从他的下一个小朋友开始 , 继续 0...m-1 报数 .... 这样下去 .... 直到剩下最后一个小朋友 , 可以不用表演。 -### 解题思路 +## 解题思路 约瑟夫环 @@ -2355,9 +2355,9 @@ public int LastRemaining_Solution(int n, int m) { } ``` -## 63. 股票的最大利润 +# 63. 股票的最大利润 -### 题目描述 +## 题目描述 可以有一次买入和一次卖出,买入必须在前。求最大收益。 @@ -2375,9 +2375,9 @@ public int maxProfit(int[] prices) { } ``` -## 64. 求 1+2+3+...+n +# 64. 求 1+2+3+...+n -### 题目描述 +## 题目描述 求 1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case 等关键字及条件判断语句(A?B:C) @@ -2400,9 +2400,9 @@ public int Add(int num1, int num2) { } ``` -## 66. 构建乘积数组 +# 66. 构建乘积数组 -### 题目描述 +## 题目描述 给定一个数组 A[0, 1,..., n-1], 请构建一个数组 B[0, 1,..., n-1], 其中 B 中的元素 B[i]=A[0]\*A[1]\*...\*A[i-1]\*A[i+1]\*...\*A[n-1]。不能使用除法。 @@ -2422,7 +2422,7 @@ public int[] multiply(int[] A) { # 两个面试案例 -## 67. 把字符串转换成整数 +# 67. 把字符串转换成整数 ```java public int StrToInt(String str) { @@ -2439,7 +2439,7 @@ public int StrToInt(String str) { } ``` -## 68. 树中两个节点的最低公共祖先 +# 68. 树中两个节点的最低公共祖先 树是二叉查找树的最低公共祖先问题: