剑指 offer 题解.md 93.1 KB
Newer Older
C
CyC2018 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
<!-- GFM-TOC -->
* [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-树的子结构)
* [27. 二叉树的镜像](#27-二叉树的镜像)
* [28 对称的二叉树](#28-对称的二叉树)
* [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-两个链表的第一个公共结点)
* [53 数字在排序数组中出现的次数](#53-数字在排序数组中出现的次数)
* [54. 二叉搜索树的第 K 个结点](#54-二叉搜索树的第-k-个结点)
* [55.1 二叉树的深度](#551-二叉树的深度)
* [55.2 平衡二叉树](#552-平衡二叉树)
* [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-求-123n)
* [65. 不用加减乘除做加法](#65-不用加减乘除做加法)
* [66. 构建乘积数组](#66-构建乘积数组)
* [67. 把字符串转换成整数](#67-把字符串转换成整数)
* [68. 树中两个节点的最低公共祖先](#68-树中两个节点的最低公共祖先)
* [参考文献](#参考文献)
<!-- GFM-TOC -->


# 2. 实现 Singleton
C
CyC2018 已提交
84

C
CyC2018 已提交
85
[单例模式](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md)
C
CyC2018 已提交
86

C
CyC2018 已提交
87
# 3. 数组中重复的数字
C
CyC2018 已提交
88

C
CyC2018 已提交
89 90
[NowCoder](https://www.nowcoder.com/practice/623a5ac0ea5b4e5f95552655361ae0a8?tpId=13&tqId=11203&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
91
## 题目描述
C
CyC2018 已提交
92

C
CyC2018 已提交
93
在一个长度为 n 的数组里的所有数字都在 0 到 n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字是重复的,也不知道每个数字重复几次。请找出数组中任意一个重复的数字。例如,如果输入长度为 7 的数组 {2, 3, 1, 0, 2, 5},那么对应的输出是第一个重复的数字 2。
C
CyC2018 已提交
94

C
CyC2018 已提交
95
要求复杂度为 O(N) + O(1),也就是时间复杂度 O(N),空间复杂度 O(1)。因此不能使用排序的方法,也不能使用额外的标记数组。牛客网讨论区这一题的首票答案使用 nums[i] + length 来将元素标记,这么做会有加法溢出问题。
C
CyC2018 已提交
96

C
CyC2018 已提交
97
## 解题思路
C
CyC2018 已提交
98

C
CyC2018 已提交
99
这种数组元素在 [0, n-1] 范围内的问题,可以将值为 i 的元素放到第 i 个位置上。
C
CyC2018 已提交
100

C
CyC2018 已提交
101
以 (2, 3, 1, 0, 2, 5) 为例:
C
CyC2018 已提交
102

C
CyC2018 已提交
103
```text-html-basic
C
CyC2018 已提交
104 105 106 107 108 109 110 111
position-0 : (2,3,1,0,2,5) // 2 <-> 1
             (1,3,2,0,2,5) // 1 <-> 3
             (3,1,2,0,2,5) // 3 <-> 0
             (0,1,2,3,2,5) // already in position
position-1 : (0,1,2,3,2,5) // already in position
position-2 : (0,1,2,3,2,5) // already in position
position-3 : (0,1,2,3,2,5) // already in position
position-4 : (0,1,2,3,2,5) // nums[i] == nums[nums[i]], exit
C
CyC2018 已提交
112 113
```

C
CyC2018 已提交
114
遍历到位置 4 时,该位置上的数为 2,但是第 2 个位置上已经有一个 2 的值了,因此可以知道 2 重复。
C
CyC2018 已提交
115 116

```java
C
CyC2018 已提交
117 118 119 120 121 122 123 124 125 126 127 128 129
public boolean duplicate(int[] nums, int length, int[] duplication) {
    if (nums == null || length <= 0)
        return false;
    for (int i = 0; i < length; i++) {
        while (nums[i] != i) {
            if (nums[i] == nums[nums[i]]) {
                duplication[0] = nums[i];
                return true;
            }
            swap(nums, i, nums[i]);
        }
    }
    return false;
C
CyC2018 已提交
130 131
}

C
CyC2018 已提交
132 133
private void swap(int[] nums, int i, int j) {
    int t = nums[i]; nums[i] = nums[j]; nums[j] = t;
C
CyC2018 已提交
134 135 136
}
```

C
CyC2018 已提交
137
# 4. 二维数组中的查找
C
CyC2018 已提交
138

C
CyC2018 已提交
139 140
[NowCoder](https://www.nowcoder.com/practice/abc3fe2ce8e146608e868a70efebf62e?tpId=13&tqId=11154&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
141
## 题目描述
C
CyC2018 已提交
142 143 144 145

在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

```html
C
CyC2018 已提交
146
Consider the following matrix:
C
CyC2018 已提交
147
[
C
CyC2018 已提交
148 149 150 151 152
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
C
CyC2018 已提交
153 154
]

C
CyC2018 已提交
155 156
Given target = 5, return true.
Given target = 20, return false.
C
CyC2018 已提交
157 158
```

C
CyC2018 已提交
159
## 解题思路
C
CyC2018 已提交
160

C
CyC2018 已提交
161
从右上角开始查找。因为矩阵中的一个数,它左边的数都比它小,下边的数都比它大。因此,从右上角开始查找,就可以根据 target 和当前元素的大小关系来缩小查找区间。
C
CyC2018 已提交
162

C
CyC2018 已提交
163
复杂度:O(M + N) + O(1)
C
CyC2018 已提交
164 165

```java
C
CyC2018 已提交
166 167 168 169 170 171 172 173 174 175 176 177 178 179
public boolean Find(int target, int[][] matrix) {
    if (matrix == null || matrix.length == 0 || matrix[0].length == 0)
        return false;
    int rows = matrix.length, cols = matrix[0].length;
    int r = 0, c = cols - 1; // 从右上角开始
    while (r <= rows - 1 && c >= 0) {
        if (target == matrix[r][c])
            return true;
        else if (target > matrix[r][c])
            r++;
        else 
            c--;
    }
    return false;
C
CyC2018 已提交
180 181 182
}
```

C
CyC2018 已提交
183
# 5. 替换空格
C
CyC2018 已提交
184

C
CyC2018 已提交
185 186
[NowCoder](https://www.nowcoder.com/practice/4060ac7e3e404ad1a894ef3e17650423?tpId=13&tqId=11155&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
187
## 题目描述
C
CyC2018 已提交
188

C
CyC2018 已提交
189
请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为 We Are Happy. 则经过替换之后的字符串为 We%20Are%20Happy。
C
CyC2018 已提交
190

C
CyC2018 已提交
191
## 解题思路
C
CyC2018 已提交
192 193 194

在字符串尾部填充任意字符,使得字符串的长度等于字符串替换之后的长度。因为一个空格要替换成三个字符(%20),因此当遍历到一个空格时,需要在尾部填充两个任意字符。

C
CyC2018 已提交
195
令 P1 指向字符串原来的末尾位置,P2 指向字符串现在的末尾位置。P1 和 P2从后向前遍历,当 P1 遍历到一个空格时,就需要令 P2 指向的位置依次填充 02%(注意是逆序的),否则就填充上 P1 指向字符的值。
C
CyC2018 已提交
196

C
CyC2018 已提交
197
从后向前遍是为了在改变 P2 所指向的内容时,不会影响到 P1 遍历原来字符串的内容。
C
CyC2018 已提交
198

C
CyC2018 已提交
199
复杂度:O(N) + O(1)
C
CyC2018 已提交
200 201

```java
C
CyC2018 已提交
202 203 204 205 206
public String replaceSpace(StringBuffer str) {
    int oldLen = str.length();
    for (int i = 0; i < oldLen; i++)
        if (str.charAt(i) == ' ')
            str.append("  ");
C
CyC2018 已提交
207

C
CyC2018 已提交
208 209 210 211 212 213 214 215 216 217 218 219
    int P1 = oldLen - 1, P2 = str.length() - 1;
    while (P1 >= 0 && P2 > P1) {
        char c = str.charAt(P1--);
        if (c == ' ') {
            str.setCharAt(P2--, '0');
            str.setCharAt(P2--, '2');
            str.setCharAt(P2--, '%');
        } else {
            str.setCharAt(P2--, c);
        }
    }
    return str.toString();
C
CyC2018 已提交
220 221 222
}
```

C
CyC2018 已提交
223
# 6. 从尾到头打印链表
C
CyC2018 已提交
224

C
CyC2018 已提交
225 226
[NowCoder](https://www.nowcoder.com/practice/d0267f7f55b3412ba93bd35cfa8e8035?tpId=13&tqId=11156&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
227
## 题目描述
C
CyC2018 已提交
228 229 230

输入链表的第一个节点,从尾到头反过来打印出每个结点的值。

C
CyC2018 已提交
231
<div align="center"> <img src="../pics//d99dc9e2-197c-4085-813d-7195da1c6762.png" width="300"/> </div><br>
C
CyC2018 已提交
232

C
CyC2018 已提交
233
## 解题思路
C
CyC2018 已提交
234

C
CyC2018 已提交
235
### 使用栈
C
CyC2018 已提交
236 237

```java
C
CyC2018 已提交
238 239 240 241 242 243 244 245 246 247
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
    Stack<Integer> stack = new Stack<>();
    while (listNode != null) {
        stack.add(listNode.val);
        listNode = listNode.next;
    }
    ArrayList<Integer> ret = new ArrayList<>();
    while (!stack.isEmpty())
        ret.add(stack.pop());
    return ret;
C
CyC2018 已提交
248 249 250
}
```

C
CyC2018 已提交
251
### 使用递归
C
CyC2018 已提交
252 253

```java
C
CyC2018 已提交
254 255 256 257 258 259 260
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
    ArrayList<Integer> ret = new ArrayList<>();
    if (listNode != null) {
        ret.addAll(printListFromTailToHead(listNode.next));
        ret.add(listNode.val);
    }
    return ret;
C
CyC2018 已提交
261 262 263
}
```

C
CyC2018 已提交
264
### 使用 Collections.reverse()
C
CyC2018 已提交
265 266

```java
C
CyC2018 已提交
267 268 269 270 271 272 273 274
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
    ArrayList<Integer> ret = new ArrayList<>();
    while (listNode != null) {
        ret.add(listNode.val);
        listNode = listNode.next;
    }
    Collections.reverse(ret);
    return ret;
C
CyC2018 已提交
275 276 277
}
```

C
CyC2018 已提交
278
### 使用头插法
C
CyC2018 已提交
279 280 281 282 283 284

利用链表头插法为逆序的特点。

头结点和第一个节点的区别:头结点是在头插法中使用的一个额外节点,这个节点不存储值;第一个节点就是链表的第一个真正存储值的节点。

```java
C
CyC2018 已提交
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
    // 头插法构建逆序链表
    ListNode head = new ListNode(-1);
    while (listNode != null) {
        ListNode memo = listNode.next;
        listNode.next = head.next;
        head.next = listNode;
        listNode = memo;
    }
    // 构建 ArrayList
    ArrayList<Integer> ret = new ArrayList<>();
    head = head.next;
    while (head != null) {
        ret.add(head.val);
        head = head.next;
    }
    return ret;
C
CyC2018 已提交
302 303 304
}
```

C
CyC2018 已提交
305
# 7. 重建二叉树
C
CyC2018 已提交
306

C
CyC2018 已提交
307 308
[NowCoder](https://www.nowcoder.com/practice/8a19cbe657394eeaac2f6ea9b0f6fcf6?tpId=13&tqId=11157&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
309
## 题目描述
C
CyC2018 已提交
310

C
CyC2018 已提交
311
根据二叉树的前序遍历和中序遍历的结果,重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
C
CyC2018 已提交
312 313

```html
C
CyC2018 已提交
314 315
preorder = [3,9,20,15,7]
inorder =  [9,3,15,20,7]
C
CyC2018 已提交
316 317
```

C
CyC2018 已提交
318
<div align="center"> <img src="../pics//8a4c6ad4-a816-47d1-b93f-7ca4f78ab67a.png" width="250"/> </div><br>
C
CyC2018 已提交
319

C
CyC2018 已提交
320
## 解题思路
C
CyC2018 已提交
321 322 323 324

前序遍历的第一个值为根节点的值,使用这个值将中序遍历结果分成两部分,左部分为树的左子树中序遍历结果,右部分为树的右子树中序遍历的结果。

```java
C
CyC2018 已提交
325
private Map<Integer, Integer> inOrderNumsIndexs = new HashMap<>(); // 缓存中序遍历数组的每个值对应的索引
C
CyC2018 已提交
326

C
CyC2018 已提交
327 328 329 330
public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
    for (int i = 0; i < in.length; i++)
        inOrderNumsIndexs.put(in[i], i);
    return reConstructBinaryTree(pre, 0, pre.length - 1, in, 0, in.length - 1);
C
CyC2018 已提交
331 332
}

C
CyC2018 已提交
333 334 335 336 337 338 339 340 341
private TreeNode reConstructBinaryTree(int[] pre, int preL, int preR, int[] in, int inL, int inR) {
    if (preL > preR)
        return null;
    TreeNode root = new TreeNode(pre[preL]);
    int inIndex = inOrderNumsIndexs.get(root.val);
    int leftTreeSize = inIndex - inL;
    root.left = reConstructBinaryTree(pre, preL + 1, preL + leftTreeSize, in, inL, inL + leftTreeSize - 1);
    root.right = reConstructBinaryTree(pre, preL + leftTreeSize + 1, preR, in, inL + leftTreeSize + 1, inR);
    return root;
C
CyC2018 已提交
342 343 344
}
```

C
CyC2018 已提交
345
# 8. 二叉树的下一个结点
C
CyC2018 已提交
346

C
CyC2018 已提交
347 348
[NowCoder](https://www.nowcoder.com/practice/9023a0c988684a53960365b889ceaf5e?tpId=13&tqId=11210&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
349
## 题目描述
C
CyC2018 已提交
350 351 352

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

C
CyC2018 已提交
353
## 解题思路
C
CyC2018 已提交
354

C
CyC2018 已提交
355
① 如果一个节点的右子树不为空,那么该节点的下一个节点是右子树的最左节点;
C
CyC2018 已提交
356

C
CyC2018 已提交
357
<div align="center"> <img src="../pics//cb0ed469-27ab-471b-a830-648b279103c8.png" width="250"/> </div><br>
C
CyC2018 已提交
358

C
CyC2018 已提交
359
② 否则,向上找第一个左链接指向的树包含该节点的祖先节点。
C
CyC2018 已提交
360

C
CyC2018 已提交
361
<div align="center"> <img src="../pics//e143f6da-d114-4ba4-8712-f65299047fa2.png" width="250"/> </div><br>
C
CyC2018 已提交
362 363

```java
C
CyC2018 已提交
364 365 366 367 368
public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;
C
CyC2018 已提交
369

C
CyC2018 已提交
370 371 372
    TreeLinkNode(int val) {
        this.val = val;
    }
C
CyC2018 已提交
373 374 375 376
}
```

```java
C
CyC2018 已提交
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
public TreeLinkNode GetNext(TreeLinkNode pNode) {
    if (pNode.right != null) {
        TreeLinkNode node = pNode.right;
        while (node.left != null)
            node = node.left;
        return node;
    } else {
        while (pNode.next != null) {
            TreeLinkNode parent = pNode.next;
            if (parent.left == pNode)
                return parent;
            pNode = pNode.next;
        }
    }
    return null;
C
CyC2018 已提交
392 393 394
}
```

C
CyC2018 已提交
395
# 9. 用两个栈实现队列
C
CyC2018 已提交
396

C
CyC2018 已提交
397 398
[NowCoder](https://www.nowcoder.com/practice/54275ddae22f475981afa2244dd448c6?tpId=13&tqId=11158&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
399
## 题目描述
C
CyC2018 已提交
400

C
CyC2018 已提交
401
用两个栈来实现一个队列,完成队列的 Push 和 Pop 操作。队列中的元素为 int 类型。
C
CyC2018 已提交
402

C
CyC2018 已提交
403
## 解题思路
C
CyC2018 已提交
404

C
CyC2018 已提交
405
in 栈用来处理入栈(push)操作,out 栈用来处理出栈(pop)操作。一个元素进入 in 栈之后,出栈的顺序被反转。当元素要出栈时,需要先进入 out 栈,此时元素出栈顺序再一次被反转,因此出栈顺序就和最开始入栈顺序是相同的,先进入的元素先退出,这就是队列的顺序。
C
CyC2018 已提交
406

C
CyC2018 已提交
407
<div align="center"> <img src="../pics//5acf7550-86c5-4c5b-b912-8ce70ef9c34e.png" width="400"/> </div><br>
C
CyC2018 已提交
408 409

```java
C
CyC2018 已提交
410 411
Stack<Integer> in = new Stack<Integer>();
Stack<Integer> out = new Stack<Integer>();
C
CyC2018 已提交
412

C
CyC2018 已提交
413 414
public void push(int node) {
    in.push(node);
C
CyC2018 已提交
415 416
}

C
CyC2018 已提交
417 418 419 420
public int pop() throws Exception {
    if (out.isEmpty())
        while (!in.isEmpty())
            out.push(in.pop());
C
CyC2018 已提交
421

C
CyC2018 已提交
422 423
    if (out.isEmpty())
        throw new Exception("queue is empty");
C
CyC2018 已提交
424

C
CyC2018 已提交
425
    return out.pop();
C
CyC2018 已提交
426 427 428
}
```

C
CyC2018 已提交
429
# 10.1 斐波那契数列
C
CyC2018 已提交
430

C
CyC2018 已提交
431 432
[NowCoder](https://www.nowcoder.com/practice/c6c7742f5ba7442aada113136ddea0c3?tpId=13&tqId=11160&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
433
## 题目描述
C
CyC2018 已提交
434

C
CyC2018 已提交
435
求菲波那契数列的第 n 项,n <= 39。
C
CyC2018 已提交
436

C
CyC2018 已提交
437
<div align="center"><img src="https://latex.codecogs.com/gif.latex?f(n)=\left\{\begin{array}{rcl}0&&{n=0}\\1&&{n=1}\\f(n-1)+f(n-2)&&{n>1}\end{array}\right."/></div> <br>
C
CyC2018 已提交
438

C
CyC2018 已提交
439
## 解题思路
C
CyC2018 已提交
440

C
CyC2018 已提交
441
如果使用递归求解,会重复计算一些子问题。例如,计算 f(10) 需要计算 f(9) 和 f(8),计算 f(9) 需要计算 f(8) 和 f(7),可以看到 f(8) 被重复计算了。
C
CyC2018 已提交
442

C
CyC2018 已提交
443
<div align="center"> <img src="../pics//955af054-8872-4569-82e7-2e10b66bc38e.png" width="250"/> </div><br>
C
CyC2018 已提交
444 445 446

递归方法是将一个问题划分成多个子问题求解,动态规划也是如此,但是动态规划会把子问题的解缓存起来,避免重复求解子问题。

C
CyC2018 已提交
447
```java
C
CyC2018 已提交
448 449 450 451 452 453 454 455
public int Fibonacci(int n) {
    if (n <= 1)
        return n;
    int[] fib = new int[n + 1];
    fib[1] = 1;
    for (int i = 2; i <= n; i++)
        fib[i] = fib[i - 1] + fib[i - 2];
    return fib[n];
C
CyC2018 已提交
456 457 458
}
```

C
CyC2018 已提交
459
考虑到第 i 项只与第 i-1 和第 i-2 项有关,因此只需要存储前两项的值就能求解第 i 项,从而将空间复杂度由 O(N) 降低为 O(1)。
C
CyC2018 已提交
460 461

```java
C
CyC2018 已提交
462 463 464 465 466 467 468 469 470 471 472
public int Fibonacci(int n) {
    if (n <= 1)
        return n;
    int pre2 = 0, pre1 = 1;
    int fib = 0;
    for (int i = 2; i <= n; i++) {
        fib = pre2 + pre1;
        pre2 = pre1;
        pre1 = fib;
    }
    return fib;
C
CyC2018 已提交
473 474 475
}
```

C
CyC2018 已提交
476
由于待求解的 n 小于 40,因此可以将前 40 项的结果先进行计算,之后就能以 O(1) 时间复杂度得到第 n 项的值了。
C
CyC2018 已提交
477

C
CyC2018 已提交
478
```java
C
CyC2018 已提交
479 480
public class Solution {
    private int[] fib = new int[40];
C
CyC2018 已提交
481

C
CyC2018 已提交
482 483 484 485 486 487
    public Solution() {
        fib[1] = 1;
        fib[2] = 2;
        for (int i = 2; i < fib.length; i++)
            fib[i] = fib[i - 1] + fib[i - 2];
    }
C
CyC2018 已提交
488

C
CyC2018 已提交
489 490 491
    public int Fibonacci(int n) {
        return fib[n];
    }
C
CyC2018 已提交
492 493 494
}
```

C
CyC2018 已提交
495
# 10.2 跳台阶
C
CyC2018 已提交
496

C
CyC2018 已提交
497 498
[NowCoder](https://www.nowcoder.com/practice/8c82a5b80378478f9484d87d1c5f12a4?tpId=13&tqId=11161&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
499
## 题目描述
C
CyC2018 已提交
500

C
CyC2018 已提交
501
一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
C
CyC2018 已提交
502

C
CyC2018 已提交
503
## 解题思路
C
CyC2018 已提交
504

C
CyC2018 已提交
505
复杂度:O(N) + O(N)
C
CyC2018 已提交
506

C
CyC2018 已提交
507
```java
C
CyC2018 已提交
508 509 510 511 512 513 514 515 516
public int JumpFloor(int n) {
    if (n == 1)
        return 1;
    int[] dp = new int[n];
    dp[0] = 1;
    dp[1] = 2;
    for (int i = 2; i < n; i++)
        dp[i] = dp[i - 1] + dp[i - 2];
    return dp[n - 1];
C
CyC2018 已提交
517 518 519
}
```

C
CyC2018 已提交
520
复杂度:O(N) + O(1)
C
CyC2018 已提交
521 522

```java
C
CyC2018 已提交
523 524 525 526 527 528 529 530 531 532 533
public int JumpFloor(int n) {
    if (n <= 1)
        return n;
    int pre2 = 0, pre1 = 1;
    int result = 0;
    for (int i = 1; i <= n; i++) {
        result = pre2 + pre1;
        pre2 = pre1;
        pre1 = result;
    }
    return result;
C
CyC2018 已提交
534 535 536
}
```

C
CyC2018 已提交
537
# 10.3 变态跳台阶
C
CyC2018 已提交
538

C
CyC2018 已提交
539 540
[NowCoder](https://www.nowcoder.com/practice/22243d016f6b47f2a6928b4313c85387?tpId=13&tqId=11162&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
541
## 题目描述
C
CyC2018 已提交
542

C
CyC2018 已提交
543
一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级... 它也可以跳上 n 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
C
CyC2018 已提交
544

C
CyC2018 已提交
545
## 解题思路
C
CyC2018 已提交
546 547

```java
C
CyC2018 已提交
548 549 550 551 552 553 554
public int JumpFloorII(int target) {
    int[] dp = new int[target];
    Arrays.fill(dp, 1);
    for (int i = 1; i < target; i++)
        for (int j = 0; j < i; j++)
            dp[i] += dp[j];
    return dp[target - 1];
C
CyC2018 已提交
555 556 557
}
```

C
CyC2018 已提交
558
# 10.4 矩形覆盖
C
CyC2018 已提交
559

C
CyC2018 已提交
560 561
[NowCoder](https://www.nowcoder.com/practice/72a5a919508a4251859fb2cfb987a0e6?tpId=13&tqId=11163&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
562
## 题目描述
C
CyC2018 已提交
563

C
CyC2018 已提交
564
我们可以用 2\*1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2\*1 的小矩形无重叠地覆盖一个 2\*n 的大矩形,总共有多少种方法?
C
CyC2018 已提交
565

C
CyC2018 已提交
566
## 解题思路
C
CyC2018 已提交
567

C
CyC2018 已提交
568
复杂度:O(N) + O(N)
C
CyC2018 已提交
569

C
CyC2018 已提交
570
```java
C
CyC2018 已提交
571 572 573 574 575 576 577 578 579
public int RectCover(int n) {
    if (n <= 2)
        return n;
    int[] dp = new int[n];
    dp[0] = 1;
    dp[1] = 2;
    for (int i = 2; i < n; i++)
        dp[i] = dp[i - 1] + dp[i - 2];
    return dp[n - 1];
C
CyC2018 已提交
580 581 582
}
```

C
CyC2018 已提交
583
复杂度:O(N) + O(1)
C
CyC2018 已提交
584 585

```java
C
CyC2018 已提交
586 587 588 589 590 591 592 593 594 595 596
public int RectCover(int n) {
    if (n <= 2)
        return n;
    int pre2 = 1, pre1 = 2;
    int result = 0;
    for (int i = 3; i <= n; i++) {
        result = pre2 + pre1;
        pre2 = pre1;
        pre1 = result;
    }
    return result;
C
CyC2018 已提交
597 598 599
}
```

C
CyC2018 已提交
600
# 11. 旋转数组的最小数字
C
CyC2018 已提交
601

C
CyC2018 已提交
602 603
[NowCoder](https://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba?tpId=13&tqId=11159&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
604
## 题目描述
C
CyC2018 已提交
605

C
CyC2018 已提交
606
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组 {3, 4, 5, 1, 2} 为 {1, 2, 3, 4, 5} 的一个旋转,该数组的最小值为 1。NOTE:给出的所有元素都大于 0,若数组大小为 0,请返回 0。
C
CyC2018 已提交
607

C
CyC2018 已提交
608
## 解题思路
C
CyC2018 已提交
609

C
CyC2018 已提交
610
当 nums[m] <= nums[h] 的情况下,说明解在 [l, m] 之间,此时令 h = m;否则解在 [m + 1, h] 之间,令 l = m + 1。
C
CyC2018 已提交
611

C
CyC2018 已提交
612
因为 h 的赋值表达式为 h = m,因此循环体的循环条件应该为 l < h,详细解释请见 [Leetcode 题解](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3.md#%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE)
C
CyC2018 已提交
613

C
CyC2018 已提交
614
复杂度:O(logN) + O(1)
C
CyC2018 已提交
615 616

```java
C
CyC2018 已提交
617 618 619 620 621 622 623 624 625 626 627 628
public int minNumberInRotateArray(int[] nums) {
    if (nums.length == 0)
        return 0;
    int l = 0, h = nums.length - 1;
    while (l < h) {
        int m = l + (h - l) / 2;
        if (nums[m] <= nums[h])
            h = m;
        else
            l = m + 1;
    }
    return nums[l];
C
CyC2018 已提交
629 630 631
}
```

C
CyC2018 已提交
632
# 12. 矩阵中的路径
C
CyC2018 已提交
633

C
CyC2018 已提交
634 635
[NowCoder](https://www.nowcoder.com/practice/c61c6999eecb4b8f88a98f66b273a3cc?tpId=13&tqId=11218&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
636
## 题目描述
C
CyC2018 已提交
637 638 639

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。

C
CyC2018 已提交
640
例如下面的矩阵包含了一条 bfce 路径。
C
CyC2018 已提交
641

C
CyC2018 已提交
642
<div align="center"> <img src="../pics//e31abb94-9201-4e06-9902-61101b92f475.png" width="300"/> </div><br>
C
CyC2018 已提交
643

C
CyC2018 已提交
644
## 解题思路
C
CyC2018 已提交
645 646

```java
C
CyC2018 已提交
647 648 649
private final static int[][] next = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
private int rows;
private int cols;
C
CyC2018 已提交
650

C
CyC2018 已提交
651 652 653 654 655 656 657 658 659 660 661 662
public boolean hasPath(char[] array, int rows, int cols, char[] str) {
    if (rows == 0 || cols == 0)
        return false;
    this.rows = rows;
    this.cols = cols;
    boolean[][] marked = new boolean[rows][cols];
    char[][] matrix = buildMatrix(array);
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < cols; j++)
            if (backtracking(matrix, str, marked, 0, i, j))
                return true;
    return false;
C
CyC2018 已提交
663 664
}

C
CyC2018 已提交
665 666 667 668 669 670 671 672 673 674 675
private boolean backtracking(char[][] matrix, char[] str, boolean[][] marked, int pathLen, int r, int c) {
    if (pathLen == str.length)
        return true;
    if (r < 0 || r >= rows || c < 0 || c >= cols || matrix[r][c] != str[pathLen] || marked[r][c])
        return false;
    marked[r][c] = true;
    for (int[] n : next)
        if (backtracking(matrix, str, marked, pathLen + 1, r + n[0], c + n[1]))
            return true;
    marked[r][c] = false;
    return false;
C
CyC2018 已提交
676 677
}

C
CyC2018 已提交
678 679 680 681 682 683
private char[][] buildMatrix(char[] array) {
    char[][] matrix = new char[rows][cols];
    for (int i = 0, idx = 0; i < rows; i++)
        for (int j = 0; j < cols; j++)
            matrix[i][j] = array[idx++];
    return matrix;
C
CyC2018 已提交
684 685 686
}
```

C
CyC2018 已提交
687
# 13. 机器人的运动范围
C
CyC2018 已提交
688

C
CyC2018 已提交
689 690
[NowCoder](https://www.nowcoder.com/practice/6e5207314b5241fb83f2329e89fdecc8?tpId=13&tqId=11219&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
691
## 题目描述
C
CyC2018 已提交
692

C
CyC2018 已提交
693
地上有一个 m 行和 n 列的方格。一个机器人从坐标 (0, 0) 的格子开始移动,每一次只能向左右上下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于 k 的格子。例如,当 k 为 18 时,机器人能够进入方格(35, 37),因为 3+5+3+7=18。但是,它不能进入方格(35, 38),因为 3+5+3+8=19。请问该机器人能够达到多少个格子?
C
CyC2018 已提交
694

C
CyC2018 已提交
695
## 解题思路
C
CyC2018 已提交
696 697

```java
C
CyC2018 已提交
698 699 700 701 702 703
private static final int[][] next = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
private int cnt = 0;
private int rows;
private int cols;
private int threshold;
private int[][] digitSum;
C
CyC2018 已提交
704

C
CyC2018 已提交
705 706 707 708 709 710 711 712
public int movingCount(int threshold, int rows, int cols) {
    this.rows = rows;
    this.cols = cols;
    this.threshold = threshold;
    initDigitSum();
    boolean[][] marked = new boolean[rows][cols];
    dfs(marked, 0, 0);
    return cnt;
C
CyC2018 已提交
713 714
}

C
CyC2018 已提交
715 716 717 718 719 720 721 722 723
private void dfs(boolean[][] marked, int r, int c) {
    if (r < 0 || r >= rows || c < 0 || c >= cols || marked[r][c])
        return;
    marked[r][c] = true;
    if (this.digitSum[r][c] > this.threshold)
        return;
    cnt++;
    for (int[] n : next)
        dfs(marked, r + n[0], c + n[1]);
C
CyC2018 已提交
724 725
}

C
CyC2018 已提交
726 727 728 729 730 731 732 733 734 735 736 737 738
private void initDigitSum() {
    int[] digitSumOne = new int[Math.max(rows, cols)];
    for (int i = 0; i < digitSumOne.length; i++) {
        int n = i;
        while (n > 0) {
            digitSumOne[i] += n % 10;
            n /= 10;
        }
    }
    digitSum = new int[rows][cols];
    for (int i = 0; i < this.rows; i++)
        for (int j = 0; j < this.cols; j++)
            digitSum[i][j] = digitSumOne[i] + digitSumOne[j];
C
CyC2018 已提交
739 740 741
}
```

C
CyC2018 已提交
742
# 14. 剪绳子
C
CyC2018 已提交
743

C
CyC2018 已提交
744 745
[Leetcode](https://leetcode.com/problems/integer-break/description/)

C
CyC2018 已提交
746
## 题目描述
C
CyC2018 已提交
747 748 749

把一根绳子剪成多段,并且使得每段的长度乘积最大。

C
CyC2018 已提交
750
For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, return 36 (10 = 3 + 3 + 4).
C
CyC2018 已提交
751

C
CyC2018 已提交
752
## 解题思路
C
CyC2018 已提交
753

C
CyC2018 已提交
754
### 动态规划解法
C
CyC2018 已提交
755 756

```java
C
CyC2018 已提交
757 758 759 760 761 762 763
public int integerBreak(int n) {
    int[] dp = new int[n + 1];
    dp[1] = 1;
    for (int i = 2; i <= n; i++)
        for (int j = 1; j < i; j++)
            dp[i] = Math.max(dp[i], Math.max(j * (i - j), dp[j] * (i - j)));
    return dp[n];
C
CyC2018 已提交
764 765 766
}
```

C
CyC2018 已提交
767
### 贪心解法
C
CyC2018 已提交
768

C
CyC2018 已提交
769
尽可能多剪长度为 3 的绳子,并且不允许有长度为 1 的绳子出现,如果出现了,就从已经切好长度为 3 的绳子中拿出一段与长度为 1 的绳子重新组合,把它们切成两段长度为 2 的绳子。
C
CyC2018 已提交
770

C
CyC2018 已提交
771
证明:当 n >= 5 时,3(n - 3) - 2(n - 2) = n - 5 >= 0。因此把长度大于 5 的绳子切成两段,令其中一段长度为 3 可以使得两段的乘积最大。
C
CyC2018 已提交
772 773

```java
C
CyC2018 已提交
774 775 776 777 778 779 780 781 782 783 784 785
public int integerBreak(int n) {
    if (n < 2)
        return 0;
    if (n == 2)
        return 1;
    if (n == 3)
        return 2;
    int timesOf3 = n / 3;
    if (n - timesOf3 * 3 == 1)
        timesOf3--;
    int timesOf2 = (n - timesOf3 * 3) / 2;
    return (int) (Math.pow(3, timesOf3)) * (int) (Math.pow(2, timesOf2));
C
CyC2018 已提交
786 787 788
}
```

C
CyC2018 已提交
789
# 15. 二进制中 1 的个数
C
CyC2018 已提交
790

C
CyC2018 已提交
791 792
[NowCoder](https://www.nowcoder.com/practice/8ee967e43c2c4ec193b040ea7fbb10b8?tpId=13&tqId=11164&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
793
## 题目描述
C
CyC2018 已提交
794

C
CyC2018 已提交
795
输入一个整数,输出该数二进制表示中 1 的个数。
C
CyC2018 已提交
796

C
CyC2018 已提交
797
### Integer.bitCount()
C
CyC2018 已提交
798 799

```java
C
CyC2018 已提交
800 801
public int NumberOf1(int n) {
    return Integer.bitCount(n);
C
CyC2018 已提交
802 803 804
}
```

C
CyC2018 已提交
805
### n&(n-1)
C
CyC2018 已提交
806

C
CyC2018 已提交
807
O(logM) 时间复杂度解法,其中 M 表示 1 的个数。
C
CyC2018 已提交
808

C
CyC2018 已提交
809
该位运算是去除 n 的位级表示中最低的那一位。
C
CyC2018 已提交
810 811

```
C
CyC2018 已提交
812 813 814
n       : 10110100
n-1     : 10110011
n&(n-1) : 10110000
C
CyC2018 已提交
815 816 817
```

```java
C
CyC2018 已提交
818 819 820 821 822 823 824
public int NumberOf1(int n) {
    int cnt = 0;
    while (n != 0) {
        cnt++;
        n &= (n - 1);
    }
    return cnt;
C
CyC2018 已提交
825 826 827
}
```

C
CyC2018 已提交
828
# 16. 数值的整数次方
C
CyC2018 已提交
829

C
CyC2018 已提交
830 831
[NowCoder](https://www.nowcoder.com/practice/1a834e5e3e1a4b7ba251417554e07c00?tpId=13&tqId=11165&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
832
## 题目描述
C
CyC2018 已提交
833

C
CyC2018 已提交
834
给定一个 double 类型的浮点数 base 和 int 类型的整数 exponent。求 base 的 exponent 次方。
C
CyC2018 已提交
835

C
CyC2018 已提交
836
## 解题思路
C
CyC2018 已提交
837

C
CyC2018 已提交
838
下面的讨论中 x 代表 base,n 代表 exponent。
C
CyC2018 已提交
839

C
CyC2018 已提交
840
<div align="center"><img src="https://latex.codecogs.com/gif.latex?x^n=\left\{\begin{array}{rcl}(x*x)^{n/2}&&{n\%2=0}\\x*(x*x)^{n/2}&&{n\%2=1}\end{array}\right."/></div> <br>
C
CyC2018 已提交
841

C
CyC2018 已提交
842
因为 (x\*x)<sup>n/2</sup> 可以通过递归求解,并且每递归一次,n 都减小一半,因此整个算法的时间复杂度为 O(logN)。
C
CyC2018 已提交
843 844

```java
C
CyC2018 已提交
845 846 847 848 849 850 851 852 853 854 855 856 857 858
public double Power(double base, int exponent) {
    if (exponent == 0)
        return 1;
    if (exponent == 1)
        return base;
    boolean isNegative = false;
    if (exponent < 0) {
        exponent = -exponent;
        isNegative = true;
    }
    double pow = Power(base * base, exponent / 2);
    if (exponent % 2 != 0)
        pow = pow * base;
    return isNegative ? 1 / pow : pow;
C
CyC2018 已提交
859 860 861
}
```

C
CyC2018 已提交
862
# 17. 打印从 1 到最大的 n 位数
C
CyC2018 已提交
863

C
CyC2018 已提交
864
## 题目描述
C
CyC2018 已提交
865

C
CyC2018 已提交
866
输入数字 n,按顺序打印出从 1 最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数即 999。
C
CyC2018 已提交
867

C
CyC2018 已提交
868
## 解题思路
C
CyC2018 已提交
869

C
CyC2018 已提交
870
由于 n 可能会非常大,因此不能直接用 int 表示数字,而是用 char 数组进行存储。
C
CyC2018 已提交
871 872 873 874

使用回溯法得到所有的数。

```java
C
CyC2018 已提交
875 876 877 878 879
public void print1ToMaxOfNDigits(int n) {
    if (n <= 0)
        return;
    char[] number = new char[n];
    print1ToMaxOfNDigits(number, -1);
C
CyC2018 已提交
880 881
}

C
CyC2018 已提交
882 883 884 885 886 887 888 889 890
private void print1ToMaxOfNDigits(char[] number, int digit) {
    if (digit == number.length - 1) {
        printNumber(number);
        return;
    }
    for (int i = 0; i < 10; i++) {
        number[digit + 1] = (char) (i + '0');
        print1ToMaxOfNDigits(number, digit + 1);
    }
C
CyC2018 已提交
891 892
}

C
CyC2018 已提交
893 894 895 896 897 898 899
private void printNumber(char[] number) {
    int index = 0;
    while (index < number.length && number[index] == '0')
        index++;
    while (index < number.length)
        System.out.print(number[index++]);
    System.out.println();
C
CyC2018 已提交
900 901 902
}
```

C
CyC2018 已提交
903
# 18.1 在 O(1) 时间内删除链表节点
C
CyC2018 已提交
904

C
CyC2018 已提交
905
## 解题思路
C
CyC2018 已提交
906

C
CyC2018 已提交
907
① 如果该节点不是尾节点,那么可以直接将下一个节点的值赋给该节点,然后令该节点指向下下个节点,再删除下一个节点,时间复杂度为 O(1)。
C
CyC2018 已提交
908

C
CyC2018 已提交
909
<div align="center"> <img src="../pics//41392d76-dd1d-4712-85d9-e8bb46b04a2d.png" width="600"/> </div><br>
C
CyC2018 已提交
910

C
CyC2018 已提交
911
② 否则,就需要先遍历链表,找到节点的前一个节点,然后让前一个节点指向 null,时间复杂度为 O(N)。
C
CyC2018 已提交
912

C
CyC2018 已提交
913
<div align="center"> <img src="../pics//db4921d4-184b-48ba-a3cf-1d1141e3ba2d.png" width="600"/> </div><br>
C
CyC2018 已提交
914

C
CyC2018 已提交
915
综上,如果进行 N 次操作,那么大约需要操作节点的次数为 N-1+N=2N-1,其中 N-1 表示 N-1 个不是尾节点的每个节点以 O(1) 的时间复杂度操作节点的总次数,N 表示 1 个尾节点以 O(N) 的时间复杂度操作节点的总次数。(2N-1)/N \~ 2,因此该算法的平均时间复杂度为 O(1)。
C
CyC2018 已提交
916 917

```java
C
CyC2018 已提交
918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
public ListNode deleteNode(ListNode head, ListNode tobeDelete) {
    if (head == null || head.next == null || tobeDelete == null)
        return null;
    if (tobeDelete.next != null) {
        // 要删除的节点不是尾节点
        ListNode next = tobeDelete.next;
        tobeDelete.val = next.val;
        tobeDelete.next = next.next;
    } else {
        ListNode cur = head;
        while (cur.next != tobeDelete)
            cur = cur.next;
        cur.next = null;
    }
    return head;
C
CyC2018 已提交
933 934 935
}
```

C
CyC2018 已提交
936
# 18.2 删除链表中重复的结点
C
CyC2018 已提交
937

C
CyC2018 已提交
938 939
[NowCoder](https://www.nowcoder.com/practice/fc533c45b73a41b0b44ccba763f866ef?tpId=13&tqId=11209&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
940
## 题目描述
C
CyC2018 已提交
941

C
CyC2018 已提交
942
<div align="center"> <img src="../pics//8433fbb2-c35c-45ef-831d-e3ca42aebd51.png" width="500"/> </div><br>
C
CyC2018 已提交
943

C
CyC2018 已提交
944
## 解题描述
C
CyC2018 已提交
945 946

```java
C
CyC2018 已提交
947 948 949 950 951 952 953 954 955 956 957 958
public ListNode deleteDuplication(ListNode pHead) {
    if (pHead == null || pHead.next == null)
        return pHead;
    ListNode next = pHead.next;
    if (pHead.val == next.val) {
        while (next != null && pHead.val == next.val)
            next = next.next;
        return deleteDuplication(next);
    } else {
        pHead.next = deleteDuplication(pHead.next);
        return pHead;
    }
C
CyC2018 已提交
959 960 961
}
```

C
CyC2018 已提交
962
# 19. 正则表达式匹配
C
CyC2018 已提交
963

C
CyC2018 已提交
964 965
[NowCoder](https://www.nowcoder.com/practice/45327ae22b7b413ea21df13ee7d6429c?tpId=13&tqId=11205&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
966
## 题目描述
C
CyC2018 已提交
967

C
CyC2018 已提交
968
请实现一个函数用来匹配包括 '.' 和 '\*' 的正则表达式。模式中的字符 '.' 表示任意一个字符,而 '\*' 表示它前面的字符可以出现任意次(包含 0 次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串 "aaa" 与模式 "a.a" 和 "ab\*ac\*a" 匹配,但是与 "aa.a" 和 "ab\*a" 均不匹配。
C
CyC2018 已提交
969

C
CyC2018 已提交
970
## 解题思路
C
CyC2018 已提交
971

C
CyC2018 已提交
972
应该注意到,'.' 是用来当做一个任意字符,而 '\*' 是用来重复前面的字符。这两个的作用不同,不能把 '.' 的作用和 '\*' 进行类比,从而把它当成重复前面字符一次。
C
CyC2018 已提交
973 974

```html
C
CyC2018 已提交
975 976 977 978 979 980 981 982 983
if p.charAt(j) == s.charAt(i)  :  then dp[i][j] = dp[i-1][j-1];
if p.charAt(j) == '.'          :  then dp[i][j] = dp[i-1][j-1];
if p.charAt(j) == '*'          :
     if p.charAt(j-1) != s.charAt(i)  :  then dp[i][j] = dp[i][j-2]  // a* only counts as empty
     if p.charAt(j-1) == s.charAt(i) or
        p.charAt(i-1) == '.'          :
              then dp[i][j] = dp[i-1][j]   // a* counts as multiple a
                or dp[i][j] = dp[i][j-1]   // a* counts as single a
                or dp[i][j] = dp[i][j-2]   // a* counts as empty
C
CyC2018 已提交
984 985 986
```

```java
C
CyC2018 已提交
987 988 989 990 991 992 993
public boolean match(char[] str, char[] pattern) {
    int m = str.length, n = pattern.length;
    boolean[][] dp = new boolean[m + 1][n + 1];
    dp[0][0] = true;
    for (int i = 1; i <= n; i++)
        if (pattern[i - 1] == '*')
            dp[0][i] = dp[0][i - 2];
C
CyC2018 已提交
994

C
CyC2018 已提交
995 996 997 998 999 1000 1001 1002 1003 1004
    for (int i = 1; i <= m; i++)
        for (int j = 1; j <= n; j++)
            if (str[i - 1] == pattern[j - 1] || pattern[j - 1] == '.')
                dp[i][j] = dp[i - 1][j - 1];
            else if (pattern[j - 1] == '*')
                if (pattern[j - 2] == str[i - 1] || pattern[j - 2] == '.')
                    dp[i][j] = dp[i][j - 1] || dp[i][j - 2] || dp[i - 1][j];
                else
                    dp[i][j] = dp[i][j - 2];
    return dp[m][n];
C
CyC2018 已提交
1005 1006 1007
}
```

C
CyC2018 已提交
1008
# 20. 表示数值的字符串
C
CyC2018 已提交
1009

C
CyC2018 已提交
1010 1011
[NowCoder](https://www.nowcoder.com/practice/6f8c901d091949a5837e24bb82a731f2?tpId=13&tqId=11206&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1012
## 题目描述
C
CyC2018 已提交
1013

C
CyC2018 已提交
1014
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串 "+100","5e2","-123","3.1416" 和 "-1E-16" 都表示数值。 但是 "12e","1a3.14","1.2.3","+-5" 和 "12e+4.3" 都不是。
C
CyC2018 已提交
1015

C
CyC2018 已提交
1016
## 解题思路
C
CyC2018 已提交
1017 1018

```java
C
CyC2018 已提交
1019 1020 1021 1022
public boolean isNumeric(char[] str) {
    if (str == null)
        return false;
    return new String(str).matches("[+-]?\\d*(\\.\\d+)?([eE][+-]?\\d+)?");
C
CyC2018 已提交
1023 1024 1025
}
```

C
CyC2018 已提交
1026
# 21. 调整数组顺序使奇数位于偶数前面
C
CyC2018 已提交
1027

C
CyC2018 已提交
1028 1029
[NowCoder](https://www.nowcoder.com/practice/beb5aa231adc45b2a5dcc5b62c93f593?tpId=13&tqId=11166&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1030
## 题目描述
C
CyC2018 已提交
1031 1032 1033

保证奇数和奇数,偶数和偶数之间的相对位置不变,这和书本不太一样。

C
CyC2018 已提交
1034
## 解题思路
C
CyC2018 已提交
1035 1036

```java
C
CyC2018 已提交
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
public void reOrderArray(int[] nums) {
    // 奇数个数
    int oddCnt = 0;
    for (int val : nums)
        if (val % 2 == 1)
            oddCnt++;
    int[] copy = nums.clone();
    int i = 0, j = oddCnt;
    for (int num : copy) {
        if (num % 2 == 1)
            nums[i++] = num;
        else
            nums[j++] = num;
    }
C
CyC2018 已提交
1051 1052 1053
}
```

C
CyC2018 已提交
1054
# 22. 链表中倒数第 K 个结点
C
CyC2018 已提交
1055

C
CyC2018 已提交
1056 1057
[NowCoder](https://www.nowcoder.com/practice/529d3ae5a407492994ad2a246518148a?tpId=13&tqId=11167&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1058
## 解题思路
C
CyC2018 已提交
1059

C
CyC2018 已提交
1060
设链表的长度为 N。设两个指针 P1 和 P2,先让 P1 移动 K 个节点,则还有 N - K 个节点可以移动。此时让 P1 和 P2 同时移动,可以知道当 P1 移动到链表结尾时,P2 移动到 N - K 个节点处,该位置就是倒数第 K 个节点。
C
CyC2018 已提交
1061

C
CyC2018 已提交
1062
<div align="center"> <img src="../pics//207c1801-2335-4b1b-b65c-126a0ba966cb.png" width="500"/> </div><br>
C
CyC2018 已提交
1063 1064

```java
C
CyC2018 已提交
1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
public ListNode FindKthToTail(ListNode head, int k) {
    if (head == null)
        return null;
    ListNode fast, slow;
    fast = slow = head;
    while (fast != null && k-- > 0)
        fast = fast.next;
    if (k > 0)
        return null;
    while (fast != null) {
        fast = fast.next;
        slow = slow.next;
    }
    return slow;
C
CyC2018 已提交
1079 1080 1081
}
```

C
CyC2018 已提交
1082
# 23. 链表中环的入口结点
C
CyC2018 已提交
1083

C
CyC2018 已提交
1084 1085
[NowCoder](https://www.nowcoder.com/practice/253d2c59ec3e4bc68da16833f79a38e4?tpId=13&tqId=11208&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1086
## 解题思路
C
CyC2018 已提交
1087

C
CyC2018 已提交
1088
使用双指针,一个指针 fast 每次移动两个节点,一个指针 slow 每次移动一个节点。因为存在环,所以两个指针必定相遇在环中的某个节点上。假设相遇点在下图的 z1 位置,此时 fast 移动的节点数为 x+2y+z,slow 为 x+y,由于 fast 速度比 slow 快一倍,因此 x+2y+z=2(x+y),得到 x=z。
C
CyC2018 已提交
1089

C
CyC2018 已提交
1090
在相遇点,slow 要到环的入口点还需要移动 z 个节点,如果让 fast 重新从头开始移动,并且速度变为每次移动一个节点,那么它到环入口点还需要移动 x 个节点。在上面已经推导出 x=z,因此 fast 和 slow 将在环入口点相遇。
C
CyC2018 已提交
1091

C
CyC2018 已提交
1092
<div align="center"> <img src="../pics//71363383-2d06-4c63-8b72-c01c2186707d.png" width="600"/> </div><br>
C
CyC2018 已提交
1093 1094

```java
C
CyC2018 已提交
1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
public ListNode EntryNodeOfLoop(ListNode pHead) {
    if (pHead == null || pHead.next == null)
        return null;
    ListNode slow = pHead, fast = pHead;
    while (fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
        if (slow == fast)
            break;
    }
    fast = pHead;
    while (slow != fast) {
        slow = slow.next;
        fast = fast.next;
    }
    return slow;
C
CyC2018 已提交
1111 1112 1113
}
```

C
CyC2018 已提交
1114
# 24. 反转链表
C
CyC2018 已提交
1115

C
CyC2018 已提交
1116 1117
[NowCoder](https://www.nowcoder.com/practice/75e878df47f24fdc9dc3e400ec6058ca?tpId=13&tqId=11168&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1118
## 解题思路
C
CyC2018 已提交
1119

C
CyC2018 已提交
1120
### 递归
C
CyC2018 已提交
1121 1122

```java
C
CyC2018 已提交
1123 1124 1125 1126 1127 1128 1129 1130
public ListNode ReverseList(ListNode head) {
    if (head == null || head.next == null)
        return head;
    ListNode next = head.next;
    head.next = null;
    ListNode newHead = ReverseList(next);
    next.next = head;
    return newHead;
C
CyC2018 已提交
1131 1132 1133
}
```

C
CyC2018 已提交
1134
### 迭代
C
CyC2018 已提交
1135 1136

```java
C
CyC2018 已提交
1137 1138 1139 1140 1141 1142 1143 1144 1145
public ListNode ReverseList(ListNode head) {
    ListNode newList = new ListNode(-1);
    while (head != null) {
        ListNode next = head.next;
        head.next = newList.next;
        newList.next = head;
        head = next;
    }
    return newList.next;
C
CyC2018 已提交
1146 1147 1148
}
```

C
CyC2018 已提交
1149
# 25. 合并两个排序的链表
C
CyC2018 已提交
1150

C
CyC2018 已提交
1151 1152
[NowCoder](https://www.nowcoder.com/practice/d8b6b4358f774294a89de2a6ac4d9337?tpId=13&tqId=11169&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1153
## 题目描述
C
CyC2018 已提交
1154

C
CyC2018 已提交
1155
<div align="center"> <img src="../pics//43f2cafa-3568-4a89-a895-4725666b94a6.png" width="500"/> </div><br>
C
CyC2018 已提交
1156

C
CyC2018 已提交
1157
## 解题思路
C
CyC2018 已提交
1158

C
CyC2018 已提交
1159
### 递归
C
CyC2018 已提交
1160 1161

```java
C
CyC2018 已提交
1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173
public ListNode Merge(ListNode list1, ListNode list2) {
    if (list1 == null)
        return list2;
    if (list2 == null)
        return list1;
    if (list1.val <= list2.val) {
        list1.next = Merge(list1.next, list2);
        return list1;
    } else {
        list2.next = Merge(list1, list2.next);
        return list2;
    }
C
CyC2018 已提交
1174 1175 1176
}
```

C
CyC2018 已提交
1177
### 迭代
C
CyC2018 已提交
1178 1179

```java
C
CyC2018 已提交
1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197
public ListNode Merge(ListNode list1, ListNode list2) {
    ListNode head = new ListNode(-1);
    ListNode cur = head;
    while (list1 != null && list2 != null) {
        if (list1.val <= list2.val) {
            cur.next = list1;
            list1 = list1.next;
        } else {
            cur.next = list2;
            list2 = list2.next;
        }
        cur = cur.next;
    }
    if (list1 != null)
        cur.next = list1;
    if (list2 != null)
        cur.next = list2;
    return head.next;
C
CyC2018 已提交
1198 1199 1200
}
```

C
CyC2018 已提交
1201
# 26. 树的子结构
C
CyC2018 已提交
1202

C
CyC2018 已提交
1203 1204
[NowCoder](https://www.nowcoder.com/practice/6e196c44c7004d15b1610b9afca8bd88?tpId=13&tqId=11170&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1205
## 题目描述
C
CyC2018 已提交
1206

C
CyC2018 已提交
1207
<div align="center"> <img src="../pics//4583e24f-424b-4d50-8a14-2c38a1827d4a.png" width="500"/> </div><br>
C
CyC2018 已提交
1208

C
CyC2018 已提交
1209
## 解题思路
C
CyC2018 已提交
1210 1211

```java
C
CyC2018 已提交
1212 1213 1214 1215
public boolean HasSubtree(TreeNode root1, TreeNode root2) {
    if (root1 == null || root2 == null)
        return false;
    return isSubtree(root1, root2) || HasSubtree(root1.left, root2) || HasSubtree(root1.right, root2);
C
CyC2018 已提交
1216 1217
}

C
CyC2018 已提交
1218 1219 1220 1221 1222 1223 1224 1225
private boolean isSubtree(TreeNode root1, TreeNode root2) {
    if (root2 == null)
        return true;
    if (root1 == null)
        return false;
    if (root1.val != root2.val)
        return false;
    return isSubtree(root1.left, root2.left) && isSubtree(root1.right, root2.right);
C
CyC2018 已提交
1226 1227 1228
}
```

C
CyC2018 已提交
1229
# 27. 二叉树的镜像
C
CyC2018 已提交
1230

C
CyC2018 已提交
1231 1232
[NowCoder](https://www.nowcoder.com/practice/564f4c26aa584921bc75623e48ca3011?tpId=13&tqId=11171&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1233
## 题目描述
C
CyC2018 已提交
1234

C
CyC2018 已提交
1235
<div align="center"> <img src="../pics//a2d13178-f1ef-4811-a240-1fe95b55b1eb.png" width="300"/> </div><br>
C
CyC2018 已提交
1236

C
CyC2018 已提交
1237
## 解题思路
C
CyC2018 已提交
1238 1239

```java
C
CyC2018 已提交
1240 1241 1242 1243 1244 1245
public void Mirror(TreeNode root) {
    if (root == null)
        return;
    swap(root);
    Mirror(root.left);
    Mirror(root.right);
C
CyC2018 已提交
1246 1247
}

C
CyC2018 已提交
1248 1249 1250 1251
private void swap(TreeNode root) {
    TreeNode t = root.left;
    root.left = root.right;
    root.right = t;
C
CyC2018 已提交
1252 1253 1254
}
```

C
CyC2018 已提交
1255
# 28 对称的二叉树
C
CyC2018 已提交
1256

C
CyC2018 已提交
1257 1258
[NowCder](https://www.nowcoder.com/practice/ff05d44dfdb04e1d83bdbdab320efbcb?tpId=13&tqId=11211&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1259
## 题目描述
C
CyC2018 已提交
1260

C
CyC2018 已提交
1261
<div align="center"> <img src="../pics//f42443e0-208d-41ea-be44-c7fd97d2e3bf.png" width="300"/> </div><br>
C
CyC2018 已提交
1262

C
CyC2018 已提交
1263
## 解题思路
C
CyC2018 已提交
1264 1265

```java
C
CyC2018 已提交
1266 1267 1268 1269
boolean isSymmetrical(TreeNode pRoot) {
    if (pRoot == null)
        return true;
    return isSymmetrical(pRoot.left, pRoot.right);
C
CyC2018 已提交
1270 1271
}

C
CyC2018 已提交
1272 1273 1274 1275 1276 1277 1278 1279
boolean isSymmetrical(TreeNode t1, TreeNode t2) {
    if (t1 == null && t2 == null)
        return true;
    if (t1 == null || t2 == null)
        return false;
    if (t1.val != t2.val)
        return false;
    return isSymmetrical(t1.left, t2.right) && isSymmetrical(t1.right, t2.left);
C
CyC2018 已提交
1280 1281 1282
}
```

C
CyC2018 已提交
1283
# 29. 顺时针打印矩阵
C
CyC2018 已提交
1284

C
CyC2018 已提交
1285 1286
[NowCoder](https://www.nowcoder.com/practice/9b4c81a02cd34f76be2659fa0d54342a?tpId=13&tqId=11172&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1287
## 题目描述
C
CyC2018 已提交
1288

C
CyC2018 已提交
1289
下图的矩阵顺时针打印结果为:1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10
C
CyC2018 已提交
1290

C
CyC2018 已提交
1291
<div align="center"> <img src="../pics//0f373947-c68f-45b4-a59e-086154745ac5.png" width="300"/> </div><br>
C
CyC2018 已提交
1292

C
CyC2018 已提交
1293
## 解题思路
C
CyC2018 已提交
1294 1295

```java
C
CyC2018 已提交
1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312
public ArrayList<Integer> printMatrix(int[][] matrix) {
    ArrayList<Integer> ret = new ArrayList<>();
    int r1 = 0, r2 = matrix.length - 1, c1 = 0, c2 = matrix[0].length - 1;
    while (r1 <= r2 && c1 <= c2) {
        for (int i = c1; i <= c2; i++)
            ret.add(matrix[r1][i]);
        for (int i = r1 + 1; i <= r2; i++)
            ret.add(matrix[i][c2]);
        if (r1 != r2)
            for (int i = c2 - 1; i >= c1; i--)
                ret.add(matrix[r2][i]);
        if (c1 != c2)
            for (int i = r2 - 1; i > r1; i--)
                ret.add(matrix[i][c1]);
        r1++; r2--; c1++; c2--;
    }
    return ret;
C
CyC2018 已提交
1313 1314 1315
}
```

C
CyC2018 已提交
1316
# 30. 包含 min 函数的栈
C
CyC2018 已提交
1317

C
CyC2018 已提交
1318 1319
[NowCoder](https://www.nowcoder.com/practice/4c776177d2c04c2494f2555c9fcc1e49?tpId=13&tqId=11173&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1320
## 题目描述
C
CyC2018 已提交
1321

C
CyC2018 已提交
1322
定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的 min 函数。
C
CyC2018 已提交
1323

C
CyC2018 已提交
1324
## 解题思路
C
CyC2018 已提交
1325 1326

```java
C
CyC2018 已提交
1327 1328
private Stack<Integer> stack = new Stack<>();
private Stack<Integer> minStack = new Stack<>();
C
CyC2018 已提交
1329

C
CyC2018 已提交
1330 1331 1332
public void push(int node) {
    stack.push(node);
    minStack.push(minStack.isEmpty() ? node : Math.min(minStack.peek(), node));
C
CyC2018 已提交
1333 1334
}

C
CyC2018 已提交
1335 1336 1337
public void pop() {
    stack.pop();
    minStack.pop();
C
CyC2018 已提交
1338 1339
}

C
CyC2018 已提交
1340 1341
public int top() {
    return stack.peek();
C
CyC2018 已提交
1342 1343
}

C
CyC2018 已提交
1344 1345
public int min() {
    return minStack.peek();
C
CyC2018 已提交
1346 1347 1348
}
```

C
CyC2018 已提交
1349
# 31. 栈的压入、弹出序列
C
CyC2018 已提交
1350

C
CyC2018 已提交
1351 1352
[NowCoder](https://www.nowcoder.com/practice/d77d11405cc7470d82554cb392585106?tpId=13&tqId=11174&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1353
## 题目描述
C
CyC2018 已提交
1354

C
CyC2018 已提交
1355
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列 1,2,3,4,5 是某栈的压入顺序,序列 4,5,3,2,1 是该压栈序列对应的一个弹出序列,但 4,3,5,1,2 就不可能是该压栈序列的弹出序列。
C
CyC2018 已提交
1356

C
CyC2018 已提交
1357
## 解题思路
C
CyC2018 已提交
1358 1359 1360 1361

使用一个栈来模拟压入弹出操作。

```java
C
CyC2018 已提交
1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372
public boolean IsPopOrder(int[] pushA, int[] popA) {
    int n = pushA.length;
    Stack<Integer> stack = new Stack<>();
    for (int pushIndex = 0, popIndex = 0; pushIndex < n; pushIndex++) {
        stack.push(pushA[pushIndex]);
        while (popIndex < n && stack.peek() == popA[popIndex]) {
            stack.pop();
            popIndex++;
        }
    }
    return stack.isEmpty();
C
CyC2018 已提交
1373 1374 1375
}
```

C
CyC2018 已提交
1376
# 32.1 从上往下打印二叉树
C
CyC2018 已提交
1377

C
CyC2018 已提交
1378 1379
[NowCoder](https://www.nowcoder.com/practice/7fe2212963db4790b57431d9ed259701?tpId=13&tqId=11175&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1380
## 题目描述
C
CyC2018 已提交
1381 1382 1383 1384 1385

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

例如,以下二叉树层次遍历的结果为:1,2,3,4,5,6,7

C
CyC2018 已提交
1386
<div align="center"> <img src="../pics//348bc2db-582e-4aca-9f88-38c40e9a0e69.png" width="250"/> </div><br>
C
CyC2018 已提交
1387

C
CyC2018 已提交
1388
## 解题思路
C
CyC2018 已提交
1389 1390 1391 1392 1393 1394

使用队列来进行层次遍历。

不需要使用两个队列分别存储当前层的节点和下一层的节点,因为在开始遍历一层的节点时,当前队列中的节点数就是当前层的节点数,只要控制遍历这么多节点数,就能保证这次遍历的都是当前层的节点。

```java
C
CyC2018 已提交
1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
    Queue<TreeNode> queue = new LinkedList<>();
    ArrayList<Integer> ret = new ArrayList<>();
    if (root == null)
        return ret;
    queue.add(root);
    while (!queue.isEmpty()) {
        int cnt = queue.size();
        while (cnt-- > 0) {
            TreeNode t = queue.poll();
            if (t.left != null)
                queue.add(t.left);
            if (t.right != null)
                queue.add(t.right);
            ret.add(t.val);
        }
    }
    return ret;
C
CyC2018 已提交
1413 1414 1415
}
```

C
CyC2018 已提交
1416
# 32.2 把二叉树打印成多行
C
CyC2018 已提交
1417

C
CyC2018 已提交
1418 1419
[NowCoder](https://www.nowcoder.com/practice/445c44d982d04483b04a54f298796288?tpId=13&tqId=11213&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1420
## 题目描述
C
CyC2018 已提交
1421 1422 1423

和上题几乎一样。

C
CyC2018 已提交
1424
## 解题思路
C
CyC2018 已提交
1425 1426

```java
C
CyC2018 已提交
1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446
ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
    ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
    if (pRoot == null)
        return ret;
    Queue<TreeNode> queue = new LinkedList<>();
    queue.add(pRoot);
    while (!queue.isEmpty()) {
        ArrayList<Integer> list = new ArrayList<>();
        int cnt = queue.size();
        while (cnt-- > 0) {
            TreeNode node = queue.poll();
            list.add(node.val);
            if (node.left != null)
                queue.add(node.left);
            if (node.right != null)
                queue.add(node.right);
        }
        ret.add(list);
    }
    return ret;
C
CyC2018 已提交
1447 1448 1449
}
```

C
CyC2018 已提交
1450
# 32.3 按之字形顺序打印二叉树
C
CyC2018 已提交
1451

C
CyC2018 已提交
1452 1453
[NowCoder](https://www.nowcoder.com/practice/91b69814117f4e8097390d107d2efbe0?tpId=13&tqId=11212&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1454
## 题目描述
C
CyC2018 已提交
1455 1456 1457

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

C
CyC2018 已提交
1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488
## 解题思路

```java
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
    ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
    if (pRoot == null)
        return ret;
    Queue<TreeNode> queue = new LinkedList<>();
    queue.add(pRoot);
    boolean reverse = false;
    while (!queue.isEmpty()) {
        ArrayList<Integer> list = new ArrayList<>();
        int cnt = queue.size();
        while (cnt-- > 0) {
            TreeNode node = queue.poll();
            list.add(node.val);
            if (node.left != null)
                queue.add(node.left);
            if (node.right != null)
                queue.add(node.right);
        }
        if (reverse)
            Collections.reverse(list);
        reverse = !reverse;
        ret.add(list);
    }
    return ret;
}
```

# 33. 二叉搜索树的后序遍历序列
C
CyC2018 已提交
1489

C
CyC2018 已提交
1490 1491
[NowCoder](https://www.nowcoder.com/practice/a861533d45854474ac791d90e447bafd?tpId=13&tqId=11176&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1492
## 题目描述
C
CyC2018 已提交
1493

C
CyC2018 已提交
1494
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。假设输入的数组的任意两个数字都互不相同。
C
CyC2018 已提交
1495

C
CyC2018 已提交
1496
例如,下图是后序遍历序列 3,1,2 所对应的二叉搜索树。
C
CyC2018 已提交
1497

C
CyC2018 已提交
1498
<div align="center"> <img src="../pics//836a4eaf-4798-4e48-b52a-a3dab9435ace.png" width="150"/> </div><br>
C
CyC2018 已提交
1499

C
CyC2018 已提交
1500
## 解题思路
C
CyC2018 已提交
1501 1502

```java
C
CyC2018 已提交
1503 1504 1505 1506
public boolean VerifySquenceOfBST(int[] sequence) {
    if (sequence == null || sequence.length == 0)
        return false;
    return verify(sequence, 0, sequence.length - 1);
C
CyC2018 已提交
1507 1508
}

C
CyC2018 已提交
1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519
private boolean verify(int[] sequence, int first, int last) {
    if (last - first <= 1)
        return true;
    int rootVal = sequence[last];
    int cutIndex = first;
    while (cutIndex < last && sequence[cutIndex] <= rootVal)
        cutIndex++;
    for (int i = cutIndex + 1; i < last; i++)
        if (sequence[i] < rootVal)
            return false;
    return verify(sequence, first, cutIndex - 1) && verify(sequence, cutIndex, last - 1);
C
CyC2018 已提交
1520 1521 1522
}
```

C
CyC2018 已提交
1523
# 34. 二叉树中和为某一值的路径
C
CyC2018 已提交
1524

C
CyC2018 已提交
1525 1526
[NowCoder](https://www.nowcoder.com/practice/b736e784e3e34731af99065031301bca?tpId=13&tqId=11177&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1527
## 题目描述
C
CyC2018 已提交
1528 1529 1530

输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

C
CyC2018 已提交
1531
下图的二叉树有两条和为 22 的路径:10, 5, 7 和 10, 12
C
CyC2018 已提交
1532

C
CyC2018 已提交
1533
<div align="center"> <img src="../pics//f5477abd-c246-4851-89ab-6b1cde2549b1.png" width="200"/> </div><br>
C
CyC2018 已提交
1534

C
CyC2018 已提交
1535
## 解题思路
C
CyC2018 已提交
1536 1537

```java
C
CyC2018 已提交
1538
private ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
C
CyC2018 已提交
1539

C
CyC2018 已提交
1540 1541 1542
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {
    backtracking(root, target, new ArrayList<>());
    return ret;
C
CyC2018 已提交
1543 1544
}

C
CyC2018 已提交
1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556
private void backtracking(TreeNode node, int target, ArrayList<Integer> path) {
    if (node == null)
        return;
    path.add(node.val);
    target -= node.val;
    if (target == 0 && node.left == null && node.right == null) {
        ret.add(new ArrayList(path));
    } else {
        backtracking(node.left, target, path);
        backtracking(node.right, target, path);
    }
    path.remove(path.size() - 1);
C
CyC2018 已提交
1557 1558 1559
}
```

C
CyC2018 已提交
1560
# 35. 复杂链表的复制
C
CyC2018 已提交
1561

C
CyC2018 已提交
1562 1563
[NowCoder](https://www.nowcoder.com/practice/f836b2c43afc4b35ad6adc41ec941dba?tpId=13&tqId=11178&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1564
## 题目描述
C
CyC2018 已提交
1565

C
CyC2018 已提交
1566
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 head。
C
CyC2018 已提交
1567

C
CyC2018 已提交
1568
<div align="center"> <img src="../pics//a01d1516-8168-461a-a24b-620b9cfc40f4.png" width="300"/> </div><br>
C
CyC2018 已提交
1569

C
CyC2018 已提交
1570
## 解题思路
C
CyC2018 已提交
1571 1572 1573

第一步,在每个节点的后面插入复制的节点。

C
CyC2018 已提交
1574
<div align="center"> <img src="../pics//2e6c72f5-3b8e-4e32-b87b-9491322628fe.png" width="600"/> </div><br>
C
CyC2018 已提交
1575

C
CyC2018 已提交
1576
第二步,对复制节点的 random 链接进行赋值。
C
CyC2018 已提交
1577

C
CyC2018 已提交
1578
<div align="center"> <img src="../pics//323ffd6c-8b54-4f3e-b361-555a6c8bf218.png" width="600"/> </div><br>
C
CyC2018 已提交
1579 1580 1581

第三步,拆分。

C
CyC2018 已提交
1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616
<div align="center"> <img src="../pics//8f3b9519-d705-48fe-87ad-2e4052fc81d2.png" width="600"/> </div><br>

```java
public RandomListNode Clone(RandomListNode pHead) {
    if (pHead == null)
        return null;
    // 插入新节点
    RandomListNode cur = pHead;
    while (cur != null) {
        RandomListNode clone = new RandomListNode(cur.label);
        clone.next = cur.next;
        cur.next = clone;
        cur = clone.next;
    }
    // 建立 random 链接
    cur = pHead;
    while (cur != null) {
        RandomListNode clone = cur.next;
        if (cur.random != null)
            clone.random = cur.random.next;
        cur = clone.next;
    }
    // 拆分
    cur = pHead;
    RandomListNode pCloneHead = pHead.next;
    while (cur.next != null) {
        RandomListNode next = cur.next;
        cur.next = next.next;
        cur = next;
    }
    return pCloneHead;
}
```

# 36. 二叉搜索树与双向链表
C
CyC2018 已提交
1617

C
CyC2018 已提交
1618 1619
[NowCoder](https://www.nowcoder.com/practice/947f6eb80d944a84850b0538bf0ec3a5?tpId=13&tqId=11179&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1620
## 题目描述
C
CyC2018 已提交
1621 1622 1623

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

C
CyC2018 已提交
1624
<div align="center"> <img src="../pics//79b12431-6d9d-4a7d-985b-1b79bc5bf5fb.png" width="400"/> </div><br>
C
CyC2018 已提交
1625

C
CyC2018 已提交
1626
## 解题思路
C
CyC2018 已提交
1627 1628

```java
C
CyC2018 已提交
1629 1630
private TreeNode pre = null;
private TreeNode head = null;
C
CyC2018 已提交
1631

C
CyC2018 已提交
1632 1633 1634 1635 1636
public TreeNode Convert(TreeNode root) {
    if (root == null)
        return null;
    inOrder(root);
    return head;
C
CyC2018 已提交
1637 1638
}

C
CyC2018 已提交
1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649
private void inOrder(TreeNode node) {
    if (node == null)
        return;
    inOrder(node.left);
    node.left = pre;
    if (pre != null)
        pre.right = node;
    pre = node;
    if (head == null)
        head = node;
    inOrder(node.right);
C
CyC2018 已提交
1650 1651 1652
}
```

C
CyC2018 已提交
1653
# 37. 序列化二叉树
C
CyC2018 已提交
1654

C
CyC2018 已提交
1655 1656
[NowCoder](https://www.nowcoder.com/practice/cf7e25aa97c04cc1a68c8f040e71fb84?tpId=13&tqId=11214&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1657
## 题目描述
C
CyC2018 已提交
1658 1659 1660

请实现两个函数,分别用来序列化和反序列化二叉树。

C
CyC2018 已提交
1661
## 解题思路
C
CyC2018 已提交
1662 1663

```java
C
CyC2018 已提交
1664
public class Solution {
C
CyC2018 已提交
1665

C
CyC2018 已提交
1666
    private String deserializeStr;
C
CyC2018 已提交
1667

C
CyC2018 已提交
1668 1669 1670 1671 1672
    public String Serialize(TreeNode root) {
        if (root == null)
            return "#";
        return root.val + " " + Serialize(root.left) + " " + Serialize(root.right); 
    }
C
CyC2018 已提交
1673

C
CyC2018 已提交
1674 1675 1676 1677
    public TreeNode Deserialize(String str) {
        deserializeStr = str;
        return Deserialize();
    }
C
CyC2018 已提交
1678

C
CyC2018 已提交
1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692
    private TreeNode Deserialize() {
        if (deserializeStr.length() == 0)
            return null;
        int index = deserializeStr.indexOf(" ");
        String node = index == -1 ? deserializeStr : deserializeStr.substring(0, index);
        deserializeStr = index == -1 ? "" : deserializeStr.substring(index + 1);
        if (node.equals("#"))
            return null;
        int val = Integer.valueOf(node);
        TreeNode t = new TreeNode(val);
        t.left = Deserialize();
        t.right = Deserialize();
        return t;
    }
C
CyC2018 已提交
1693 1694 1695
}
```

C
CyC2018 已提交
1696
# 38. 字符串的排列
C
CyC2018 已提交
1697

C
CyC2018 已提交
1698 1699
[NowCoder](https://www.nowcoder.com/practice/fe6b651b66ae47d7acce78ffdd9a96c7?tpId=13&tqId=11180&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1700
## 题目描述
C
CyC2018 已提交
1701

C
CyC2018 已提交
1702
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串 abc,则打印出由字符 a, b, c 所能排列出来的所有字符串 abc, acb, bac, bca, cab 和 cba。
C
CyC2018 已提交
1703

C
CyC2018 已提交
1704
## 解题思路
C
CyC2018 已提交
1705 1706

```java
C
CyC2018 已提交
1707
private ArrayList<String> ret = new ArrayList<>();
C
CyC2018 已提交
1708

C
CyC2018 已提交
1709 1710 1711 1712 1713 1714 1715
public ArrayList<String> Permutation(String str) {
    if (str.length() == 0)
        return ret;
    char[] chars = str.toCharArray();
    Arrays.sort(chars);
    backtracking(chars, new boolean[chars.length], new StringBuffer());
    return ret;
C
CyC2018 已提交
1716 1717
}

C
CyC2018 已提交
1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733
private void backtracking(char[] chars, boolean[] hasUsed, StringBuffer s) {
    if (s.length() == chars.length) {
        ret.add(s.toString());
        return;
    }
    for (int i = 0; i < chars.length; i++) {
        if (hasUsed[i])
            continue;
        if (i != 0 && chars[i] == chars[i - 1] && !hasUsed[i - 1]) // 保证不重复
            continue;
        hasUsed[i] = true;
        s.append(chars[i]);
        backtracking(chars, hasUsed, s);
        s.deleteCharAt(s.length() - 1);
        hasUsed[i] = false;
    }
C
CyC2018 已提交
1734 1735 1736
}
```

C
CyC2018 已提交
1737
# 39. 数组中出现次数超过一半的数字
C
CyC2018 已提交
1738

C
CyC2018 已提交
1739 1740
[NowCoder](https://www.nowcoder.com/practice/e8a1b01a2df14cb2b228b30ee6a92163?tpId=13&tqId=11181&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1741
## 解题思路
C
CyC2018 已提交
1742

C
CyC2018 已提交
1743
多数投票问题,可以利用 Boyer-Moore Majority Vote Algorithm 来解决这个问题,使得时间复杂度为 O(N)。
C
CyC2018 已提交
1744

C
CyC2018 已提交
1745
使用 cnt 来统计一个元素出现的次数,当遍历到的元素和统计元素不相等时,令 cnt--。如果前面查找了 i 个元素,且 cnt == 0 ,说明前 i 个元素没有 majority,或者有 majority,但是出现的次数少于 i / 2 ,因为如果多于 i / 2 的话 cnt 就一定不会为 0 。此时剩下的 n - i 个元素中,majority 的数目依然多于 (n - i) / 2,因此继续查找就能找出 majority。
C
CyC2018 已提交
1746 1747

```java
C
CyC2018 已提交
1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761
public int MoreThanHalfNum_Solution(int[] nums) {
    int majority = nums[0];
    for (int i = 1, cnt = 1; i < nums.length; i++) {
        cnt = nums[i] == majority ? cnt + 1 : cnt - 1;
        if (cnt == 0) {
            majority = nums[i];
            cnt = 1;
        }
    }
    int cnt = 0;
    for (int val : nums)
        if (val == majority)
            cnt++;
    return cnt > nums.length / 2 ? majority : 0;
C
CyC2018 已提交
1762 1763 1764
}
```

C
CyC2018 已提交
1765
# 40. 最小的 K 个数
C
CyC2018 已提交
1766

C
CyC2018 已提交
1767 1768
[NowCoder](https://www.nowcoder.com/practice/6a296eb82cf844ca8539b57c23e6e9bf?tpId=13&tqId=11182&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1769
## 解题思路
C
CyC2018 已提交
1770

C
CyC2018 已提交
1771
### 快速选择
C
CyC2018 已提交
1772

C
CyC2018 已提交
1773 1774
- 复杂度:O(N) + O(1)
- 只有当允许修改数组元素时才可以使用
C
CyC2018 已提交
1775

C
CyC2018 已提交
1776
快速排序的 partition() 方法,会返回一个整数 j 使得 a[l..j-1] 小于等于 a[j],且 a[j+1..h] 大于等于 a[j],此时 a[j] 就是数组的第 j 大元素。可以利用这个特性找出数组的第 K 个元素,这种找第 K 个元素的算法称为快速选择算法。
C
CyC2018 已提交
1777 1778

```java
C
CyC2018 已提交
1779 1780 1781 1782 1783 1784 1785 1786 1787
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k) {
    ArrayList<Integer> ret = new ArrayList<>();
    if (k > nums.length || k <= 0)
        return ret;
    int kthSmallest = findKthSmallest(nums, k - 1);
    // findKthSmallest 会改变数组,使得前 k 个数都是最小的 k 个数
    for (int i = 0; i < k; i++)
        ret.add(nums[i]);
    return ret;
C
CyC2018 已提交
1788
}
C
CyC2018 已提交
1789

C
CyC2018 已提交
1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801
public int findKthSmallest(int[] nums, int k) {
    int l = 0, h = nums.length - 1;
    while (l < h) {
        int j = partition(nums, l, h);
        if (j == k)
            break;
        if (j > k)
            h = j - 1;
        else
            l = j + 1;
    }
    return nums[k];
C
CyC2018 已提交
1802
}
C
CyC2018 已提交
1803

C
CyC2018 已提交
1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816
private int partition(int[] nums, int l, int h) {
    // 切分元素
    int parti = nums[l];
    int i = l, j = h + 1;
    while (true) {
        while (i != h && nums[++i] < parti) ;
        while (j != l && nums[--j] > parti) ;
        if (i >= j)
            break;
        swap(nums, i, j);
    }
    swap(nums, l, j);
    return j;
C
CyC2018 已提交
1817
}
C
CyC2018 已提交
1818

C
CyC2018 已提交
1819 1820 1821 1822
private void swap(int[] nums, int i, int j) {
    int t = nums[i];
    nums[i] = nums[j];
    nums[j] = t;
C
CyC2018 已提交
1823
}
C
CyC2018 已提交
1824 1825
```

C
CyC2018 已提交
1826
### 大小为 K 的最小堆
C
CyC2018 已提交
1827

C
CyC2018 已提交
1828 1829
- 复杂度:O(NlogK) + O(K)
- 特别适合处理海量数据
C
CyC2018 已提交
1830 1831 1832

应该使用大顶堆来维护最小堆,而不能直接创建一个小顶堆并设置一个大小,企图让小顶堆中的元素都是最小元素。

C
CyC2018 已提交
1833
维护一个大小为 K 的最小堆过程如下:在添加一个元素之后,如果大顶堆的大小大于 K,那么需要将大顶堆的堆顶元素去除。
C
CyC2018 已提交
1834 1835

```java
C
CyC2018 已提交
1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k) {
    if (k > nums.length || k <= 0)
        return new ArrayList<>();
    PriorityQueue<Integer> maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1);
    for (int num : nums) {
        maxHeap.add(num);
        if (maxHeap.size() > k)
            maxHeap.poll();
    }
    ArrayList<Integer> ret = new ArrayList<>(maxHeap);
    return ret;
C
CyC2018 已提交
1847 1848 1849
}
```

C
CyC2018 已提交
1850
# 41.1 数据流中的中位数
C
CyC2018 已提交
1851

C
CyC2018 已提交
1852 1853
[NowCoder](https://www.nowcoder.com/practice/9be0172896bd43948f8a32fb954e1be1?tpId=13&tqId=11216&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1854
## 题目描述
C
CyC2018 已提交
1855 1856 1857

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

C
CyC2018 已提交
1858
## 解题思路
C
CyC2018 已提交
1859 1860

```java
C
CyC2018 已提交
1861 1862 1863 1864 1865 1866 1867
public class Solution {
    // 大顶堆,存储左半边元素
    private PriorityQueue<Integer> left = new PriorityQueue<>((o1, o2) -> o2 - o1);
    // 小顶堆,存储右半边元素,并且右半边元素都大于左半边
    private PriorityQueue<Integer> right = new PriorityQueue<>();
    // 当前数据流读入的元素个数
    private int N = 0;
C
CyC2018 已提交
1868

C
CyC2018 已提交
1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882
    public void Insert(Integer val) {
        // 插入要保证两个堆存于平衡状态
        if (N % 2 == 0) {
            // N 为偶数的情况下插入到右半边。
            // 因为右半边元素都要大于左半边,但是新插入的元素不一定比左半边元素来的大,
            // 因此需要先将元素插入左半边,然后利用左半边为大顶堆的特点,取出堆顶元素即为最大元素,此时插入右半边
            left.add(val);
            right.add(left.poll());
        } else {
            right.add(val);
            left.add(right.poll());
        }
        N++;
    }
C
CyC2018 已提交
1883

C
CyC2018 已提交
1884 1885 1886 1887 1888 1889
    public Double GetMedian() {
        if (N % 2 == 0)
            return (left.peek() + right.peek()) / 2.0;
        else
            return (double) right.peek();
    }
C
CyC2018 已提交
1890 1891 1892
}
```

C
CyC2018 已提交
1893
# 41.2 字符流中第一个不重复的字符
C
CyC2018 已提交
1894

C
CyC2018 已提交
1895 1896
[NowCoder](https://www.nowcoder.com/practice/00de97733b8e4f97a3fb5c680ee10720?tpId=13&tqId=11207&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1897
## 题目描述
C
CyC2018 已提交
1898

C
CyC2018 已提交
1899
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符 "go" 时,第一个只出现一次的字符是 "g"。当从该字符流中读出前六个字符“google" 时,第一个只出现一次的字符是 "l"。
C
CyC2018 已提交
1900

C
CyC2018 已提交
1901
## 解题思路
C
CyC2018 已提交
1902 1903

```java
C
CyC2018 已提交
1904 1905
private int[] cnts = new int[256];
private Queue<Character> queue = new LinkedList<>();
C
CyC2018 已提交
1906

C
CyC2018 已提交
1907 1908 1909 1910 1911
public void Insert(char ch) {
    cnts[ch]++;
    queue.add(ch);
    while (!queue.isEmpty() && cnts[queue.peek()] > 1)
        queue.poll();
C
CyC2018 已提交
1912
}
C
CyC2018 已提交
1913

C
CyC2018 已提交
1914 1915
public char FirstAppearingOnce() {
    return queue.isEmpty() ? '#' : queue.peek();
C
CyC2018 已提交
1916 1917 1918
}
```

C
CyC2018 已提交
1919
# 42. 连续子数组的最大和
C
CyC2018 已提交
1920

C
CyC2018 已提交
1921 1922
[NowCoder](https://www.nowcoder.com/practice/459bd355da1549fa8a49e350bf3df484?tpId=13&tqId=11183&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
1923
## 题目描述
C
CyC2018 已提交
1924

C
CyC2018 已提交
1925
{6,-3,-2,7,-15,1,2,2},连续子数组的最大和为 8(从第 0 个开始,到第 3 个为止)。
C
CyC2018 已提交
1926

C
CyC2018 已提交
1927
## 解题思路
C
CyC2018 已提交
1928 1929

```java
C
CyC2018 已提交
1930 1931 1932 1933 1934 1935 1936 1937 1938 1939
public int FindGreatestSumOfSubArray(int[] nums) {
    if (nums == null || nums.length == 0)
        return 0;
    int ret = Integer.MIN_VALUE;
    int sum = 0;
    for (int val : nums) {
        sum = sum <= 0 ? val : sum + val;
        ret = Math.max(ret, sum);
    }
    return ret;
C
CyC2018 已提交
1940
}
C
CyC2018 已提交
1941 1942
```

C
CyC2018 已提交
1943
# 43. 从 1 到 n 整数中 1 出现的次数
C
CyC2018 已提交
1944

C
CyC2018 已提交
1945
[NowCoder](https://www.nowcoder.com/practice/bd7f978302044eee894445e244c7eee6?tpId=13&tqId=11184&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
C
CyC2018 已提交
1946

C
CyC2018 已提交
1947
## 解题思路
C
CyC2018 已提交
1948 1949

```java
C
CyC2018 已提交
1950 1951 1952 1953 1954 1955 1956
public int NumberOf1Between1AndN_Solution(int n) {
    int cnt = 0;
    for (int m = 1; m <= n; m *= 10) {
        int a = n / m, b = n % m;
        cnt += (a + 8) / 10 * m + (a % 10 == 1 ? b + 1 : 0);
    }
    return cnt;
C
CyC2018 已提交
1957 1958 1959
}
```

C
CyC2018 已提交
1960
> [Leetcode : 233. Number of Digit One](https://leetcode.com/problems/number-of-digit-one/discuss/64381/4+-lines-O(log-n)-C++JavaPython)
C
CyC2018 已提交
1961

C
CyC2018 已提交
1962
# 44. 数字序列中的某一位数字
C
CyC2018 已提交
1963

C
CyC2018 已提交
1964
## 题目描述
C
CyC2018 已提交
1965

C
CyC2018 已提交
1966
数字以 0123456789101112131415... 的格式序列化到一个字符串中,求这个字符串的第 index 位。
C
CyC2018 已提交
1967

C
CyC2018 已提交
1968
## 解题思路
C
CyC2018 已提交
1969 1970

```java
C
CyC2018 已提交
1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982
public int digitAtIndex(int index) {
    if (index < 0)
        return -1;
    int digit = 1;
    while (true) {
        int amount = getAmountOfDigit(digit);
        int totalAmount = amount * digit;
        if (index < totalAmount)
            return digitAtIndex(index, digit);
        index -= totalAmount;
        digit++;
    }
C
CyC2018 已提交
1983 1984 1985
}

/**
C
CyC2018 已提交
1986 1987 1988 1989 1990 1991 1992
 * digit 位数的数字组成的字符串长度
 * 例如 digit = 2,return 90
 */
private int getAmountOfDigit(int digit) {
    if (digit == 1)
        return 10;
    return (int) Math.pow(10, digit - 1) * 9;
C
CyC2018 已提交
1993 1994 1995
}

/**
C
CyC2018 已提交
1996 1997 1998 1999 2000 2001
 * 在 digit 位数组成的字符串中,第 index 个数
 */
private int digitAtIndex(int index, int digit) {
    int number = beginNumber(digit) + index / digit;
    int remain = index % digit;
    return (number + "").charAt(remain) - '0';
C
CyC2018 已提交
2002 2003 2004
}

/**
C
CyC2018 已提交
2005 2006 2007 2008 2009 2010 2011
 * digit 位数的起始数字
 * 例如 digit = 2 return 10
 */
private int beginNumber(int digit) {
    if (digit == 1)
        return 0;
    return (int) Math.pow(10, digit - 1);
C
CyC2018 已提交
2012 2013 2014
}
```

C
CyC2018 已提交
2015
# 45. 把数组排成最小的数
C
CyC2018 已提交
2016

C
CyC2018 已提交
2017 2018
[NowCoder](https://www.nowcoder.com/practice/8fecd3f8ba334add803bf2a06af1b993?tpId=13&tqId=11185&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2019
## 题目描述
C
CyC2018 已提交
2020

C
CyC2018 已提交
2021
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组 {3,32,321},则打印出这三个数字能排成的最小数字为 321323。
C
CyC2018 已提交
2022

C
CyC2018 已提交
2023
## 解题思路
C
CyC2018 已提交
2024

C
CyC2018 已提交
2025
可以看成是一个排序问题,在比较两个字符串 S1 和 S2 的大小时,应该比较的是 S1+S2 和 S2+S1 的大小,如果 S1+S2 < S2+S1,那么应该把 S1 排在前面,否则应该把 S2 排在前面。
C
CyC2018 已提交
2026 2027

```java
C
CyC2018 已提交
2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039
public String PrintMinNumber(int[] numbers) {
    if (numbers == null || numbers.length == 0)
        return "";
    int n = numbers.length;
    String[] nums = new String[n];
    for (int i = 0; i < n; i++)
        nums[i] = numbers[i] + "";
    Arrays.sort(nums, (s1, s2) -> (s1 + s2).compareTo(s2 + s1));
    String ret = "";
    for (String str : nums)
        ret += str;
    return ret;
C
CyC2018 已提交
2040 2041 2042
}
```

C
CyC2018 已提交
2043
# 46. 把数字翻译成字符串
C
CyC2018 已提交
2044

C
CyC2018 已提交
2045 2046
[Leetcode](https://leetcode.com/problems/decode-ways/description/)

C
CyC2018 已提交
2047
## 题目描述
C
CyC2018 已提交
2048

C
CyC2018 已提交
2049
给定一个数字,按照如下规则翻译成字符串:0 翻译成“a”,1 翻译成“b”... 25 翻译成“z”。一个数字有多种翻译可能,例如 12258 一共有 5 种,分别是 bccfi,bwfi,bczi,mcfi,mzi。实现一个函数,用来计算一个数字有多少种不同的翻译方法。
C
CyC2018 已提交
2050

C
CyC2018 已提交
2051
## 解题思路
C
CyC2018 已提交
2052 2053

```java
C
CyC2018 已提交
2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071
public int numDecodings(String s) {
    if (s == null || s.length() == 0)
        return 0;
    int n = s.length();
    int[] dp = new int[n + 1];
    dp[0] = 1;
    dp[1] = s.charAt(0) == '0' ? 0 : 1;
    for (int i = 2; i <= n; i++) {
        int one = Integer.valueOf(s.substring(i - 1, i));
        if (one != 0)
            dp[i] += dp[i - 1];
        if (s.charAt(i - 2) == '0')
            continue;
        int two = Integer.valueOf(s.substring(i - 2, i));
        if (two <= 26)
            dp[i] += dp[i - 2];
    }
    return dp[n];
C
CyC2018 已提交
2072 2073 2074
}
```

C
CyC2018 已提交
2075
# 47. 礼物的最大价值
C
CyC2018 已提交
2076

C
CyC2018 已提交
2077 2078
[NowCoder](https://www.nowcoder.com/questionTerminal/72a99e28381a407991f2c96d8cb238ab)

C
CyC2018 已提交
2079
## 题目描述
C
CyC2018 已提交
2080

C
CyC2018 已提交
2081
在一个 m\*n 的棋盘的每一个格都放有一个礼物,每个礼物都有一定价值(大于 0)。从左上角开始拿礼物,每次向右或向下移动一格,直到右下角结束。给定一个棋盘,求拿到礼物的最大价值。例如,对于如下棋盘
C
CyC2018 已提交
2082 2083

```
C
CyC2018 已提交
2084 2085 2086 2087
1    10   3    8
12   2    9    6
5    7    4    11
3    7    16   5
C
CyC2018 已提交
2088 2089
```

C
CyC2018 已提交
2090
礼物的最大价值为 1+12+5+7+7+16+5=53。
C
CyC2018 已提交
2091

C
CyC2018 已提交
2092
## 解题思路
C
CyC2018 已提交
2093 2094 2095 2096

应该用动态规划求解,而不是深度优先搜索,深度优先搜索过于复杂,不是最优解。

```java
C
CyC2018 已提交
2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107
public int getMost(int[][] values) {
    if (values == null || values.length == 0 || values[0].length == 0)
        return 0;
    int m = values.length, n = values[0].length;
    int[] dp = new int[n];
    for (int i = 0; i < m; i++) {
        dp[0] += values[i][0];
        for (int j = 1; j < n; j++)
            dp[j] = Math.max(dp[j], dp[j - 1]) + values[i][j];
    }
    return dp[n - 1];
C
CyC2018 已提交
2108 2109 2110
}
```

C
CyC2018 已提交
2111
# 48. 最长不含重复字符的子字符串
C
CyC2018 已提交
2112

C
CyC2018 已提交
2113
## 题目描述
C
CyC2018 已提交
2114

C
CyC2018 已提交
2115
输入一个字符串(只包含 a\~z 的字符),求其最长不含重复字符的子字符串的长度。例如对于 arabcacfr,最长不含重复字符的子字符串为 acfr,长度为 4。
C
CyC2018 已提交
2116

C
CyC2018 已提交
2117
## 解题思路
C
CyC2018 已提交
2118 2119

```java
C
CyC2018 已提交
2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137
public int longestSubStringWithoutDuplication(String str) {
    int curLen = 0;
    int maxLen = 0;
    int[] preIndexs = new int[26];
    Arrays.fill(preIndexs, -1);
    for (int i = 0; i < str.length(); i++) {
        int c = str.charAt(i) - 'a';
        int preIndex = preIndexs[c];
        if (preIndex == -1 || i - preIndex > curLen) {
            curLen++;
        } else {
            maxLen = Math.max(maxLen, curLen);
            curLen = i - preIndex;
        }
        preIndexs[c] = i;
    }
    maxLen = Math.max(maxLen, curLen);
    return maxLen;
C
CyC2018 已提交
2138 2139 2140
}
```

C
CyC2018 已提交
2141
# 49. 丑数
C
CyC2018 已提交
2142

C
CyC2018 已提交
2143 2144
[NowCoder](https://www.nowcoder.com/practice/6aa9e04fc3794f68acf8778237ba065b?tpId=13&tqId=11186&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2145
## 题目描述
C
CyC2018 已提交
2146

C
CyC2018 已提交
2147
把只包含因子 2、3 和 5 的数称作丑数(Ugly Number)。例如 6、8 都是丑数,但 14 不是,因为它包含因子 7。 习惯上我们把 1 当做是第一个丑数。求按从小到大的顺序的第 N 个丑数。
C
CyC2018 已提交
2148

C
CyC2018 已提交
2149
## 解题思路
C
CyC2018 已提交
2150 2151

```java
C
CyC2018 已提交
2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168
public int GetUglyNumber_Solution(int index) {
    if (index <= 6)
        return index;
    int i2 = 0, i3 = 0, i5 = 0;
    int[] dp = new int[index];
    dp[0] = 1;
    for (int i = 1; i < index; i++) {
        int n2 = dp[i2] * 2, n3 = dp[i3] * 3, n5 = dp[i5] * 5;
        dp[i] = Math.min(n2, Math.min(n3, n5));
        if (dp[i] == n2)
            i2++;
        if (dp[i] == n3)
            i3++;
        if (dp[i] == n5)
            i5++;
    }
    return dp[index - 1];
C
CyC2018 已提交
2169 2170 2171
}
```

C
CyC2018 已提交
2172
# 50. 第一个只出现一次的字符位置
C
CyC2018 已提交
2173

C
CyC2018 已提交
2174 2175
[NowCoder](https://www.nowcoder.com/practice/1c82e8cf713b4bbeb2a5b31cf5b0417c?tpId=13&tqId=11187&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2176
## 题目描述
C
CyC2018 已提交
2177

C
CyC2018 已提交
2178
在一个字符串 (1 <= 字符串长度 <= 10000,全部由字母组成) 中找到第一个只出现一次的字符,并返回它的位置。
C
CyC2018 已提交
2179

C
CyC2018 已提交
2180
## 解题思路
C
CyC2018 已提交
2181

C
CyC2018 已提交
2182
最直观的解法是使用 HashMap 对出现次数进行统计,但是考虑到要统计的字符范围有限,因此可以使用整型数组代替 HashMap。
C
CyC2018 已提交
2183 2184

```java
C
CyC2018 已提交
2185 2186 2187 2188 2189 2190 2191 2192
public int FirstNotRepeatingChar(String str) {
    int[] cnts = new int[256];
    for (int i = 0; i < str.length(); i++)
        cnts[str.charAt(i)]++;
    for (int i = 0; i < str.length(); i++)
        if (cnts[str.charAt(i)] == 1)
            return i;
    return -1;
C
CyC2018 已提交
2193 2194 2195
}
```

C
CyC2018 已提交
2196
以上实现的空间复杂度还不是最优的。考虑到只需要找到只出现一次的字符,那么我们只需要统计的次数信息只有 0,1,更大,使用两个比特位就能存储这些信息。
C
CyC2018 已提交
2197 2198

```java
C
CyC2018 已提交
2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213
public int FirstNotRepeatingChar(String str) {
    BitSet bs1 = new BitSet(256);
    BitSet bs2 = new BitSet(256);
    for (char c : str.toCharArray()) {
        if (!bs1.get(c) && !bs2.get(c))
            bs1.set(c);     // 0 0
        else if (bs1.get(c) && !bs2.get(c))
            bs2.set(c);     // 0 1
    }
    for (int i = 0; i < str.length(); i++) {
        char c = str.charAt(i);
        if (bs1.get(c) && !bs2.get(c))
            return i;
    }
    return -1;
C
CyC2018 已提交
2214 2215 2216
}
```

C
CyC2018 已提交
2217
# 51. 数组中的逆序对
C
CyC2018 已提交
2218

C
CyC2018 已提交
2219 2220
[NowCoder](https://www.nowcoder.com/practice/96bd6684e04a44eb80e6a68efc0ec6c5?tpId=13&tqId=11188&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2221
## 题目描述
C
CyC2018 已提交
2222

C
CyC2018 已提交
2223
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数 P。
C
CyC2018 已提交
2224

C
CyC2018 已提交
2225
## 解题思路
C
CyC2018 已提交
2226 2227

```java
C
CyC2018 已提交
2228 2229
private long cnt = 0;
private int[] tmp; // 在这里创建辅助数组,而不是在 merge() 递归函数中创建
C
CyC2018 已提交
2230

C
CyC2018 已提交
2231 2232 2233 2234
public int InversePairs(int[] nums) {
    tmp = new int[nums.length];
    mergeSort(nums, 0, nums.length - 1);
    return (int) (cnt % 1000000007);
C
CyC2018 已提交
2235 2236
}

C
CyC2018 已提交
2237 2238 2239 2240 2241 2242 2243
private void mergeSort(int[] nums, int l, int h) {
    if (h - l < 1)
        return;
    int m = l + (h - l) / 2;
    mergeSort(nums, l, m);
    mergeSort(nums, m + 1, h);
    merge(nums, l, m, h);
C
CyC2018 已提交
2244 2245
}

C
CyC2018 已提交
2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262
private void merge(int[] nums, int l, int m, int h) {
    int i = l, j = m + 1, k = l;
    while (i <= m || j <= h) {
        if (i > m)
            tmp[k] = nums[j++];
        else if (j > h)
            tmp[k] = nums[i++];
        else if (nums[i] < nums[j])
            tmp[k] = nums[i++];
        else {
            tmp[k] = nums[j++];
            this.cnt += m - i + 1; // a[i] > a[j],说明 a[i...mid] 都大于 a[j]
        }
        k++;
    }
    for (k = l; k <= h; k++)
        nums[k] = tmp[k];
C
CyC2018 已提交
2263 2264 2265
}
```

C
CyC2018 已提交
2266
# 52. 两个链表的第一个公共结点
C
CyC2018 已提交
2267

C
CyC2018 已提交
2268 2269
[NowCoder](https://www.nowcoder.com/practice/6ab1d9a29e88450685099d45c9e31e46?tpId=13&tqId=11189&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2270
## 题目描述
C
CyC2018 已提交
2271

C
CyC2018 已提交
2272
<div align="center"> <img src="../pics//8f6f9dc9-9ecd-47c8-b50e-2814f0219056.png" width="500"/> </div><br>
C
CyC2018 已提交
2273

C
CyC2018 已提交
2274
## 解题思路
C
CyC2018 已提交
2275

C
CyC2018 已提交
2276
设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。
C
CyC2018 已提交
2277

C
CyC2018 已提交
2278
当访问 A 链表的指针访问到链表尾部时,令它从链表 B 的头部重新开始访问链表 B;同样地,当访问 B 链表的指针访问到链表尾部时,令它从链表 A 的头部重新开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。
C
CyC2018 已提交
2279 2280

```java
C
CyC2018 已提交
2281 2282 2283 2284 2285 2286 2287
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
    ListNode l1 = pHead1, l2 = pHead2;
    while (l1 != l2) {
        l1 = (l1 == null) ? pHead2 : l1.next;
        l2 = (l2 == null) ? pHead1 : l2.next;
    }
    return l1;
C
CyC2018 已提交
2288 2289 2290
}
```

C
CyC2018 已提交
2291
# 53 数字在排序数组中出现的次数
C
CyC2018 已提交
2292

C
CyC2018 已提交
2293 2294
[NowCoder](https://www.nowcoder.com/practice/70610bf967994b22bb1c26f9ae901fa2?tpId=13&tqId=11190&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2295
## 题目描述
C
CyC2018 已提交
2296 2297 2298

```html
Input:
C
CyC2018 已提交
2299
1, 2, 3, 3, 3, 3, 4, 6
C
CyC2018 已提交
2300 2301 2302 2303 2304
3
Output:
4
```

C
CyC2018 已提交
2305
## 解题思路
C
CyC2018 已提交
2306 2307

```java
C
CyC2018 已提交
2308 2309 2310 2311
public int GetNumberOfK(int[] nums, int K) {
    int first = binarySearch(nums, K);
    int last = binarySearch(nums, K + 1);
    return (first == nums.length || nums[first] != K) ? 0 : last - first;
C
CyC2018 已提交
2312 2313
}

C
CyC2018 已提交
2314 2315 2316 2317 2318 2319 2320 2321 2322 2323
private int binarySearch(int[] nums, int K) {
    int l = 0, h = nums.length;
    while (l < h) {
        int m = l + (h - l) / 2;
        if (nums[m] >= K)
            h = m;
        else
            l = m + 1;
    }
    return l;
C
CyC2018 已提交
2324 2325 2326
}
```

C
CyC2018 已提交
2327
# 54. 二叉搜索树的第 K 个结点
C
CyC2018 已提交
2328

C
CyC2018 已提交
2329 2330
[NowCoder](https://www.nowcoder.com/practice/ef068f602dde4d28aab2b210e859150a?tpId=13&tqId=11215&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2331
## 解题思路
C
CyC2018 已提交
2332 2333 2334 2335

利用二叉搜索数中序遍历有序的特点。

```java
C
CyC2018 已提交
2336 2337
private TreeNode ret;
private int cnt = 0;
C
CyC2018 已提交
2338

C
CyC2018 已提交
2339 2340 2341
public TreeNode KthNode(TreeNode pRoot, int k) {
    inOrder(pRoot, k);
    return ret;
C
CyC2018 已提交
2342 2343
}

C
CyC2018 已提交
2344 2345 2346 2347 2348 2349 2350 2351
private void inOrder(TreeNode root, int k) {
    if (root == null || cnt >= k)
        return;
    inOrder(root.left, k);
    cnt++;
    if (cnt == k)
        ret = root;
    inOrder(root.right, k);
C
CyC2018 已提交
2352 2353 2354
}
```

C
CyC2018 已提交
2355
# 55.1 二叉树的深度
C
CyC2018 已提交
2356

C
CyC2018 已提交
2357 2358
[NowCoder](https://www.nowcoder.com/practice/435fb86331474282a3499955f0a41e8b?tpId=13&tqId=11191&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2359
## 题目描述
C
CyC2018 已提交
2360 2361 2362

从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

C
CyC2018 已提交
2363
<div align="center"> <img src="../pics//b29f8971-9cb8-480d-b986-0e60c2ece069.png" width="350"/> </div><br>
C
CyC2018 已提交
2364

C
CyC2018 已提交
2365
## 解题思路
C
CyC2018 已提交
2366 2367

```java
C
CyC2018 已提交
2368 2369
public int TreeDepth(TreeNode root) {
    return root == null ? 0 : 1 + Math.max(TreeDepth(root.left), TreeDepth(root.right));
C
CyC2018 已提交
2370 2371 2372
}
```

C
CyC2018 已提交
2373
# 55.2 平衡二叉树
C
CyC2018 已提交
2374

C
CyC2018 已提交
2375 2376
[NowCoder](https://www.nowcoder.com/practice/8b3b95850edb4115918ecebdf1b4d222?tpId=13&tqId=11192&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2377
## 题目描述
C
CyC2018 已提交
2378

C
CyC2018 已提交
2379
平衡二叉树左右子树高度差不超过 1。
C
CyC2018 已提交
2380

C
CyC2018 已提交
2381
<div align="center"> <img src="../pics//e026c24d-00fa-4e7c-97a8-95a98cdc383a.png" width="300"/> </div><br>
C
CyC2018 已提交
2382

C
CyC2018 已提交
2383
## 解题思路
C
CyC2018 已提交
2384 2385

```java
C
CyC2018 已提交
2386
private boolean isBalanced = true;
C
CyC2018 已提交
2387

C
CyC2018 已提交
2388 2389 2390
public boolean IsBalanced_Solution(TreeNode root) {
    height(root);
    return isBalanced;
C
CyC2018 已提交
2391 2392
}

C
CyC2018 已提交
2393 2394 2395 2396 2397 2398 2399 2400
private int height(TreeNode root) {
    if (root == null)
        return 0;
    int left = height(root.left);
    int right = height(root.right);
    if (Math.abs(left - right) > 1)
        isBalanced = false;
    return 1 + Math.max(left, right);
C
CyC2018 已提交
2401 2402 2403
}
```

C
CyC2018 已提交
2404
# 56. 数组中只出现一次的数字
C
CyC2018 已提交
2405

C
CyC2018 已提交
2406 2407
[NowCoder](https://www.nowcoder.com/practice/e02fdb54d7524710a7d664d082bb7811?tpId=13&tqId=11193&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2408
## 题目描述
C
CyC2018 已提交
2409

C
CyC2018 已提交
2410 2411
一个整型数组里除了两个数字之外,其他的数字都出现了两次,找出这两个数。

C
CyC2018 已提交
2412
## 解题思路
C
CyC2018 已提交
2413 2414 2415 2416 2417

两个不相等的元素在位级表示上必定会有一位存在不同。

将数组的所有元素异或得到的结果为不存在重复的两个元素异或的结果。

C
CyC2018 已提交
2418
diff &= -diff 得到出 diff 最右侧不为 0 的位,也就是不存在重复的两个元素在位级表示上最右侧不同的那一位,利用这一位就可以将两个元素区分开来。
C
CyC2018 已提交
2419 2420

```java
C
CyC2018 已提交
2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433
    public void FindNumsAppearOnce(int[] nums, int num1[], int num2[]) {
        int diff = 0;
        for (int num : nums)
            diff ^= num;
        // 得到最右一位
        diff &= -diff;
        for (int num : nums) {
            if ((num & diff) == 0)
                num1[0] ^= num;
            else
                num2[0] ^= num;
        }
    }
C
CyC2018 已提交
2434 2435
```

C
CyC2018 已提交
2436
# 57.1 和为 S 的两个数字
C
CyC2018 已提交
2437

C
CyC2018 已提交
2438 2439
[NowCoder](https://www.nowcoder.com/practice/390da4f7a00f44bea7c2f3d19491311b?tpId=13&tqId=11195&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2440
## 题目描述
C
CyC2018 已提交
2441

C
CyC2018 已提交
2442
输入一个递增排序的数组和一个数字 S,在数组中查找两个数,使得他们的和正好是 S,如果有多对数字的和等于 S,输出两个数的乘积最小的。
C
CyC2018 已提交
2443

C
CyC2018 已提交
2444
## 解题思路
C
CyC2018 已提交
2445 2446 2447

使用双指针,一个指针指向元素较小的值,一个指针指向元素较大的值。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。

C
CyC2018 已提交
2448 2449 2450
- 如果两个指针指向元素的和 sum == target,那么得到要求的结果;
- 如果 sum > target,移动较大的元素,使 sum 变小一些;
- 如果 sum < target,移动较小的元素,使 sum 变大一些。
C
CyC2018 已提交
2451 2452

```java
C
CyC2018 已提交
2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464
public ArrayList<Integer> FindNumbersWithSum(int[] array, int sum) {
    int i = 0, j = array.length - 1;
    while (i < j) {
        int cur = array[i] + array[j];
        if (cur == sum)
            return new ArrayList<>(Arrays.asList(array[i], array[j]));
        if (cur < sum)
            i++;
        else
            j--;
    }
    return new ArrayList<>();
C
CyC2018 已提交
2465 2466 2467
}
```

C
CyC2018 已提交
2468
# 57.2 和为 S 的连续正数序列
C
CyC2018 已提交
2469

C
CyC2018 已提交
2470 2471
[NowCoder](https://www.nowcoder.com/practice/c451a3fd84b64cb19485dad758a55ebe?tpId=13&tqId=11194&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2472
## 题目描述
C
CyC2018 已提交
2473

C
CyC2018 已提交
2474
输出所有和为 S 的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
C
CyC2018 已提交
2475

C
CyC2018 已提交
2476
例如和为 100 的连续序列有:
C
CyC2018 已提交
2477 2478

```
C
CyC2018 已提交
2479 2480
[9, 10, 11, 12, 13, 14, 15, 16]
[18, 19, 20, 21, 22]。
C
CyC2018 已提交
2481
```
C
CyC2018 已提交
2482

C
CyC2018 已提交
2483
## 解题思路
C
CyC2018 已提交
2484 2485

```java
C
CyC2018 已提交
2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508
public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
    ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
    int start = 1, end = 2;
    int curSum = 3;
    while (end < sum) {
        if (curSum > sum) {
            curSum -= start;
            start++;
        } else if (curSum < sum) {
            end++;
            curSum += end;
        } else {
            ArrayList<Integer> list = new ArrayList<>();
            for (int i = start; i <= end; i++)
                list.add(i);
            ret.add(list);
            curSum -= start;
            start++;
            end++;
            curSum += end;
        }
    }
    return ret;
C
CyC2018 已提交
2509 2510 2511
}
```

C
CyC2018 已提交
2512
# 58.1 翻转单词顺序列
C
CyC2018 已提交
2513

C
CyC2018 已提交
2514 2515
[NowCoder](https://www.nowcoder.com/practice/3194a4f4cf814f63919d0790578d51f3?tpId=13&tqId=11197&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2516
## 题目描述
C
CyC2018 已提交
2517

C
CyC2018 已提交
2518
输入:"I am a student."
C
CyC2018 已提交
2519

C
CyC2018 已提交
2520
输出:"student. a am I"
C
CyC2018 已提交
2521

C
CyC2018 已提交
2522
## 解题思路
C
CyC2018 已提交
2523

C
CyC2018 已提交
2524
题目应该有一个隐含条件,就是不能用额外的空间。虽然 Java 的题目输入参数为 String 类型,需要先创建一个字符数组使得空间复杂度为 O(N),但是正确的参数类型应该和原书一样,为字符数组,并且只能使用该字符数组的空间。任何使用了额外空间的解法在面试时都会大打折扣,包括递归解法。
C
CyC2018 已提交
2525 2526

正确的解法应该是和书上一样,先旋转每个单词,再旋转整个字符串。
C
CyC2018 已提交
2527 2528

```java
C
CyC2018 已提交
2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541
public String ReverseSentence(String str) {
    int n = str.length();
    char[] chars = str.toCharArray();
    int i = 0, j = 0;
    while (j <= n) {
        if (j == n || chars[j] == ' ') {
            reverse(chars, i, j - 1);
            i = j + 1;
        }
        j++;
    }
    reverse(chars, 0, n - 1);
    return new String(chars);
C
CyC2018 已提交
2542 2543
}

C
CyC2018 已提交
2544 2545 2546
private void reverse(char[] c, int i, int j) {
    while (i < j)
        swap(c, i++, j--);
C
CyC2018 已提交
2547
}
C
CyC2018 已提交
2548

C
CyC2018 已提交
2549 2550 2551 2552
private void swap(char[] c, int i, int j) {
    char t = c[i];
    c[i] = c[j];
    c[j] = t;
C
CyC2018 已提交
2553
}
C
CyC2018 已提交
2554 2555
```

C
CyC2018 已提交
2556
# 58.2 左旋转字符串
C
CyC2018 已提交
2557

C
CyC2018 已提交
2558 2559
[NowCoder](https://www.nowcoder.com/practice/12d959b108cb42b1ab72cef4d36af5ec?tpId=13&tqId=11196&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2560
## 题目描述
C
CyC2018 已提交
2561

C
CyC2018 已提交
2562
对于一个给定的字符序列 S,请你把其循环左移 K 位后的序列输出。例如,字符序列 S=”abcXYZdef”, 要求输出循环左移 3 位后的结果,即“XYZdefabc”。
C
CyC2018 已提交
2563

C
CyC2018 已提交
2564
## 解题思路
C
CyC2018 已提交
2565

C
CyC2018 已提交
2566
将 "abcXYZdef" 旋转左移三位,可以先将 "abc" 和 "XYZdef" 分别旋转,得到 "cbafedZYX",然后再把整个字符串旋转得到 "XYZdefabc"。
C
CyC2018 已提交
2567

C
CyC2018 已提交
2568
```java
C
CyC2018 已提交
2569 2570 2571 2572 2573 2574 2575 2576
public String LeftRotateString(String str, int n) {
    if (n >= str.length())
        return str;
    char[] chars = str.toCharArray();
    reverse(chars, 0, n - 1);
    reverse(chars, n, chars.length - 1);
    reverse(chars, 0, chars.length - 1);
    return new String(chars);
C
CyC2018 已提交
2577 2578
}

C
CyC2018 已提交
2579 2580 2581
private void reverse(char[] chars, int i, int j) {
    while (i < j)
        swap(chars, i++, j--);
C
CyC2018 已提交
2582
}
C
CyC2018 已提交
2583

C
CyC2018 已提交
2584 2585 2586 2587
private void swap(char[] chars, int i, int j) {
    char t = chars[i];
    chars[i] = chars[j];
    chars[j] = t;
C
CyC2018 已提交
2588
}
C
CyC2018 已提交
2589 2590
```

C
CyC2018 已提交
2591
# 59. 滑动窗口的最大值
C
CyC2018 已提交
2592

C
CyC2018 已提交
2593 2594
[NowCoder](https://www.nowcoder.com/practice/1624bc35a45c42c0bc17d17fa0cba788?tpId=13&tqId=11217&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2595
## 题目描述
C
CyC2018 已提交
2596

C
CyC2018 已提交
2597
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组 {2, 3, 4, 2, 6, 2, 5, 1} 及滑动窗口的大小 3,那么一共存在 6 个滑动窗口,他们的最大值分别为 {4, 4, 6, 6, 6, 5}。
C
CyC2018 已提交
2598

C
CyC2018 已提交
2599
## 解题思路
C
CyC2018 已提交
2600 2601

```java
C
CyC2018 已提交
2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615
public ArrayList<Integer> maxInWindows(int[] num, int size) {
    ArrayList<Integer> ret = new ArrayList<>();
    PriorityQueue<Integer> heap = new PriorityQueue<Integer>((o1, o2) -> o2 - o1);
    if (size > num.length || size < 1)
        return ret;
    for (int i = 0; i < size; i++)
        heap.add(num[i]);
    ret.add(heap.peek());
    for (int i = 1, j = i + size - 1; j < num.length; i++, j++) {
        heap.remove(num[i - 1]);
        heap.add(num[j]);
        ret.add(heap.peek());
    }
    return ret;
C
CyC2018 已提交
2616 2617 2618
}
```

C
CyC2018 已提交
2619
# 60. n 个骰子的点数
C
CyC2018 已提交
2620

C
CyC2018 已提交
2621 2622
[Lintcode](https://www.lintcode.com/en/problem/dices-sum/)

C
CyC2018 已提交
2623
## 题目描述
C
CyC2018 已提交
2624

C
CyC2018 已提交
2625
把 n 个骰子仍在地上,求点数和为 s 的概率。
C
CyC2018 已提交
2626

C
CyC2018 已提交
2627
## 解题思路
C
CyC2018 已提交
2628

C
CyC2018 已提交
2629
### 动态规划解法
C
CyC2018 已提交
2630

C
CyC2018 已提交
2631
使用一个二维数组 dp 存储点数出现的次数,其中 dp[i][j] 表示前 i 个骰子产生点数 j 的次数。
C
CyC2018 已提交
2632

C
CyC2018 已提交
2633 2634 2635
空间复杂度:O(N<sup>2</sup>)

```java
C
CyC2018 已提交
2636 2637 2638 2639 2640 2641
public List<Map.Entry<Integer, Double>> dicesSum(int n) {
    final int face = 6;
    final int pointNum = face * n;
    long[][] dp = new long[n + 1][pointNum + 1];
    for (int i = 1; i <= face; i++)
        dp[1][i] = 1;
C
CyC2018 已提交
2642

C
CyC2018 已提交
2643 2644 2645 2646
    for (int i = 2; i <= n; i++)
        for (int j = i; j <= pointNum; j++)  // 使用 i 个骰子最小点数为 i
            for (int k = 1; k <= face && k <= j; k++)
                dp[i][j] += dp[i - 1][j - k];
C
CyC2018 已提交
2647

C
CyC2018 已提交
2648 2649 2650 2651 2652
    final double totalNum = Math.pow(6, n);
    List<Map.Entry<Integer, Double>> ret = new ArrayList<>();
    for (int i = n; i <= pointNum; i++)
        ret.add(new AbstractMap.SimpleEntry<>(i, dp[n][i] / totalNum));
    return ret;
C
CyC2018 已提交
2653 2654 2655
}
```

C
CyC2018 已提交
2656
### 动态规划解法 + 旋转数组
C
CyC2018 已提交
2657 2658 2659 2660

空间复杂度:O(N)

```java
C
CyC2018 已提交
2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679
public List<Map.Entry<Integer, Double>> dicesSum(int n) {
    final int face = 6;
    final int pointNum = face * n;
    long[][] dp = new long[2][pointNum + 1];
    for (int i = 1; i <= face; i++)
        dp[0][i] = 1;
    int flag = 1;
    for (int i = 2; i <= n; i++, flag = 1 - flag) {
        for (int j = 0; j <= pointNum; j++)
            dp[flag][j] = 0; // 旋转数组清零
        for (int j = i; j <= pointNum; j++)  // 使用 i 个骰子最小点数为 i
            for (int k = 1; k <= face && k <= j; k++)
                dp[flag][j] += dp[1 - flag][j - k];
    }
    final double totalNum = Math.pow(6, n);
    List<Map.Entry<Integer, Double>> ret = new ArrayList<>();
    for (int i = n; i <= pointNum; i++)
        ret.add(new AbstractMap.SimpleEntry<>(i, dp[1 - flag][i] / totalNum));
    return ret;
C
CyC2018 已提交
2680 2681 2682
}
```

C
CyC2018 已提交
2683
# 61. 扑克牌顺子
C
CyC2018 已提交
2684

C
CyC2018 已提交
2685 2686
[NowCoder](https://www.nowcoder.com/practice/762836f4d43d43ca9deb273b3de8e1f4?tpId=13&tqId=11198&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2687
## 题目描述
C
CyC2018 已提交
2688

C
CyC2018 已提交
2689
五张牌,其中大小鬼为癞子,牌面大小为 0。判断是否能组成顺子。
C
CyC2018 已提交
2690

C
CyC2018 已提交
2691
## 解题思路
C
CyC2018 已提交
2692 2693

```java
C
CyC2018 已提交
2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707
public boolean isContinuous(int[] nums) {
    if (nums.length < 5)
        return false;
    Arrays.sort(nums);
    int cnt = 0;
    for (int num : nums)
        if (num == 0)
            cnt++;
    for (int i = cnt; i < nums.length - 1; i++) {
        if (nums[i + 1] == nums[i])
            return false;
        cnt -= nums[i + 1] - nums[i] - 1;
    }
    return cnt >= 0;
C
CyC2018 已提交
2708 2709 2710
}
```

C
CyC2018 已提交
2711
# 62. 圆圈中最后剩下的数
C
CyC2018 已提交
2712

C
CyC2018 已提交
2713 2714
[NowCoder](https://www.nowcoder.com/practice/f78a359491e64a50bce2d89cff857eb6?tpId=13&tqId=11199&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2715
## 题目描述
C
CyC2018 已提交
2716

C
CyC2018 已提交
2717
让小朋友们围成一个大圈。然后,他随机指定一个数 m,让编号为 0 的小朋友开始报数。每次喊到 m-1 的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续 0...m-1 报数 .... 这样下去 .... 直到剩下最后一个小朋友,可以不用表演。
C
CyC2018 已提交
2718

C
CyC2018 已提交
2719
## 解题思路
C
CyC2018 已提交
2720

C
CyC2018 已提交
2721
约瑟夫环,圆圈长度为 n 的解可以看成长度为 n-1 的解再加上报数的长度 m。因为是圆圈,所以最后需要对 n 取余。
C
CyC2018 已提交
2722 2723

```java
C
CyC2018 已提交
2724 2725 2726 2727 2728 2729
public int LastRemaining_Solution(int n, int m) {
    if (n == 0)
        return -1;
    if (n == 1)
        return 0;
    return (LastRemaining_Solution(n - 1, m) + m) % n;
C
CyC2018 已提交
2730 2731 2732
}
```

C
CyC2018 已提交
2733
# 63. 股票的最大利润
C
CyC2018 已提交
2734

C
CyC2018 已提交
2735 2736
[Leetcode](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/description/)

C
CyC2018 已提交
2737
## 题目描述
C
CyC2018 已提交
2738

C
CyC2018 已提交
2739 2740
可以有一次买入和一次卖出,买入必须在前。求最大收益。

C
CyC2018 已提交
2741
## 解题思路
C
CyC2018 已提交
2742

C
CyC2018 已提交
2743
使用贪心策略,假设第 i 轮进行卖出操作,买入操作价格应该在 i 之前并且价格最低。
C
CyC2018 已提交
2744 2745

```java
C
CyC2018 已提交
2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756
public int maxProfit(int[] prices) {
    if (prices == null || prices.length == 0)
        return 0;
    int n = prices.length;
    int soFarMin = prices[0];
    int maxProfit = 0;
    for (int i = 1; i < n; i++) {
        soFarMin = Math.min(soFarMin, prices[i]);
        maxProfit = Math.max(maxProfit, prices[i] - soFarMin);
    }
    return maxProfit;
C
CyC2018 已提交
2757 2758 2759
}
```

C
CyC2018 已提交
2760
# 64. 求 1+2+3+...+n
C
CyC2018 已提交
2761

C
CyC2018 已提交
2762 2763
[NowCoder](https://www.nowcoder.com/practice/7a0da8fc483247ff8800059e12d7caf1?tpId=13&tqId=11200&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2764
## 题目描述
C
CyC2018 已提交
2765

C
CyC2018 已提交
2766
求 1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case 等关键字及条件判断语句(A?B:C)。
C
CyC2018 已提交
2767

C
CyC2018 已提交
2768
## 解题思路
C
CyC2018 已提交
2769

C
CyC2018 已提交
2770
使用递归解法最重要的是指定返回条件,但是本题无法直接使用 if 语句来指定返回条件。
C
CyC2018 已提交
2771

C
CyC2018 已提交
2772
条件与 && 具有短路原则,即在第一个条件语句为 false 的情况下不会去执行第二个条件语句。利用这一特性,将递归的返回条件取非然后作为 && 的第一个条件语句,递归的主体转换为第二个条件语句,那么当递归的返回条件为 true 的情况下就不会执行递归的主体部分,递归返回。
C
CyC2018 已提交
2773

C
CyC2018 已提交
2774
以下实现中,递归的返回条件为 n <= 0,取非后就是 n > 0,递归的主体部分为 sum += Sum_Solution(n - 1),转换为条件语句后就是 (sum += Sum_Solution(n - 1)) > 0。
C
CyC2018 已提交
2775

C
CyC2018 已提交
2776
```java
C
CyC2018 已提交
2777 2778 2779 2780
public int Sum_Solution(int n) {
    int sum = n;
    boolean b = (n > 0) && ((sum += Sum_Solution(n - 1)) > 0);
    return sum;
C
CyC2018 已提交
2781 2782 2783
}
```

C
CyC2018 已提交
2784
# 65. 不用加减乘除做加法
C
CyC2018 已提交
2785

C
CyC2018 已提交
2786 2787
[NowCoder](https://www.nowcoder.com/practice/59ac416b4b944300b617d4f7f111b215?tpId=13&tqId=11201&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2788
## 题目描述
C
CyC2018 已提交
2789

C
CyC2018 已提交
2790
写一个函数,求两个整数之和,要求在函数体内不得使用 +、-、\*、/ 四则运算符号。
C
CyC2018 已提交
2791

C
CyC2018 已提交
2792
## 解题思路
C
CyC2018 已提交
2793

C
CyC2018 已提交
2794
a ^ b 表示没有考虑进位的情况下两数的和,(a & b) << 1 就是进位。
C
CyC2018 已提交
2795

C
CyC2018 已提交
2796
递归会终止的原因是 (a & b) << 1 最右边会多一个 0,那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,递归终止。
C
CyC2018 已提交
2797 2798

```java
C
CyC2018 已提交
2799 2800
public int Add(int num1, int num2) {
    return num2 == 0 ? num1 : Add(num1 ^ num2, (num1 & num2) << 1);
C
CyC2018 已提交
2801 2802 2803
}
```

C
CyC2018 已提交
2804
# 66. 构建乘积数组
C
CyC2018 已提交
2805

C
CyC2018 已提交
2806 2807
[NowCoder](https://www.nowcoder.com/practice/94a4d381a68b47b7a8bed86f2975db46?tpId=13&tqId=11204&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2808
## 题目描述
C
CyC2018 已提交
2809

C
CyC2018 已提交
2810
给定一个数组 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]。不能使用除法。
C
CyC2018 已提交
2811

C
CyC2018 已提交
2812
## 解题思路
C
CyC2018 已提交
2813 2814

```java
C
CyC2018 已提交
2815 2816 2817 2818 2819 2820 2821 2822
public int[] multiply(int[] A) {
    int n = A.length;
    int[] B = new int[n];
    for (int i = 0, product = 1; i < n; product *= A[i], i++)
        B[i] = product;
    for (int i = n - 1, product = 1; i >= 0; product *= A[i], i--)
        B[i] *= product;
    return B;
C
CyC2018 已提交
2823 2824 2825
}
```

C
CyC2018 已提交
2826
# 67. 把字符串转换成整数
C
CyC2018 已提交
2827

C
CyC2018 已提交
2828 2829
[NowCoder](https://www.nowcoder.com/practice/1277c681251b4372bdef344468e4f26e?tpId=13&tqId=11202&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)

C
CyC2018 已提交
2830
## 题目描述
C
CyC2018 已提交
2831

C
CyC2018 已提交
2832
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为 0 或者字符串不是一个合法的数值则返回 0。
C
CyC2018 已提交
2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843

```html
Iuput:
+2147483647
1a33

Output:
2147483647
0
```

C
CyC2018 已提交
2844
## 解题思路
C
CyC2018 已提交
2845 2846

```java
C
CyC2018 已提交
2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860
public int StrToInt(String str) {
    if (str == null || str.length() == 0)
        return 0;
    boolean isNegative = str.charAt(0) == '-';
    int ret = 0;
    for (int i = 0; i < str.length(); i++) {
        char c = str.charAt(i);
        if (i == 0 && (c == '+' || c == '-'))
            continue;
        if (c < '0' || c > '9')
            return 0; // 非法输入
        ret = ret * 10 + (c - '0');
    }
    return isNegative ? -ret : ret;
C
CyC2018 已提交
2861 2862 2863
}
```

C
CyC2018 已提交
2864
# 68. 树中两个节点的最低公共祖先
C
CyC2018 已提交
2865

C
CyC2018 已提交
2866
## 解题思路
C
CyC2018 已提交
2867

C
CyC2018 已提交
2868
### 二叉查找树
C
CyC2018 已提交
2869

C
CyC2018 已提交
2870
<div align="center"> <img src="../pics//293d2af9-de1d-403e-bed0-85d029383528.png" width="300"/> </div><br>
C
CyC2018 已提交
2871

C
CyC2018 已提交
2872
[Leetcode : 235. Lowest Common Ancestor of a Binary Search Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/description/)
C
CyC2018 已提交
2873

C
CyC2018 已提交
2874
二叉查找树中,两个节点 p, q 的公共祖先 root 满足 root.val >= p.val && root.val <= q.val。
C
CyC2018 已提交
2875 2876

```java
C
CyC2018 已提交
2877 2878 2879 2880 2881 2882 2883 2884
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if (root == null)
        return root;
    if (root.val > p.val && root.val > q.val)
        return lowestCommonAncestor(root.left, p, q);
    if (root.val < p.val && root.val < q.val)
        return lowestCommonAncestor(root.right, p, q);
    return root;
C
CyC2018 已提交
2885 2886 2887
}
```

C
CyC2018 已提交
2888
### 普通二叉树
C
CyC2018 已提交
2889

C
CyC2018 已提交
2890
<div align="center"> <img src="../pics//37a72755-4890-4b42-9eab-b0084e0c54d9.png" width="300"/> </div><br>
C
CyC2018 已提交
2891

C
CyC2018 已提交
2892
[Leetcode : 236. Lowest Common Ancestor of a Binary Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/description/)
C
CyC2018 已提交
2893

C
CyC2018 已提交
2894 2895 2896
在左右子树中查找两个节点的最低公共祖先,如果在其中一颗子树中查找到,那么就返回这个解,否则可以认为根节点就是最低公共祖先。

```java
C
CyC2018 已提交
2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if (root == null || root == p || root == q)
        return root;
    TreeNode left = lowestCommonAncestor(root.left, p, q);
    TreeNode right = lowestCommonAncestor(root.right, p, q);
    return left == null ? right : right == null ? left : root;
}
```

# 参考文献

- 何海涛. 剑指 Offer[M]. 电子工业出版社, 2012.