剑指 offer 题解.md 93.1 KB
Newer Older
C
CyC2018 已提交
1
<!-- GFM-TOC -->
C
CyC2018 已提交
2
* [1. 前言](#1-前言)
C
CyC2018 已提交
3 4 5 6 7 8 9 10 11 12
* [2. 实现 Singleton](#2-实现-singleton)
* [3. 数组中重复的数字](#3-数组中重复的数字)
* [4. 二维数组中的查找](#4-二维数组中的查找)
* [5. 替换空格](#5-替换空格)
* [6. 从尾到头打印链表](#6-从尾到头打印链表)
* [7. 重建二叉树](#7-重建二叉树)
* [8. 二叉树的下一个结点](#8-二叉树的下一个结点)
* [9. 用两个栈实现队列](#9-用两个栈实现队列)
* [10.1 斐波那契数列](#101-斐波那契数列)
* [10.2 跳台阶](#102-跳台阶)
C
CyC2018 已提交
13 14
* [10.3 矩形覆盖](#103-矩形覆盖)
* [10.4 变态跳台阶](#104-变态跳台阶)
C
CyC2018 已提交
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
* [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-两个链表的第一个公共结点)
C
CyC2018 已提交
61 62
* [53. 数字在排序数组中出现的次数](#53-数字在排序数组中出现的次数)
* [54. 二叉查找树的第 K 个结点](#54-二叉查找树的第-k-个结点)
C
CyC2018 已提交
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
* [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 -->


C
CyC2018 已提交
84 85 86 87 88 89 90
# 1. 前言

本文的绘图可通过以下途径免费获得并使用:

- [ProcessOn](https://www.processon.com/view/5a3e4c7be4b0909c1aa18b49)
- [DrawIO](https://drive.google.com/file/d/1nSSCpPUC05MFoeFuf_aeTtkm7dG5-bJ1/view?usp=sharing)

C
CyC2018 已提交
91
# 2. 实现 Singleton
C
CyC2018 已提交
92

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

C
CyC2018 已提交
95
# 3. 数组中重复的数字
C
CyC2018 已提交
96

C
CyC2018 已提交
97 98
[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 已提交
99
## 题目描述
C
CyC2018 已提交
100

C
CyC2018 已提交
101
在一个长度为 n 的数组里的所有数字都在 0 到 n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字是重复的,也不知道每个数字重复几次。请找出数组中任意一个重复的数字。
C
CyC2018 已提交
102

C
CyC2018 已提交
103 104 105 106 107 108 109
```html
Input:
{2, 3, 1, 0, 2, 5}

Output:
2
```
C
CyC2018 已提交
110

C
CyC2018 已提交
111
## 解题思路
C
CyC2018 已提交
112

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

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

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

C
CyC2018 已提交
119
```text
C
CyC2018 已提交
120 121 122 123 124 125 126 127
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 已提交
128 129
```

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

```java
C
CyC2018 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145
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 已提交
146 147
}

C
CyC2018 已提交
148
private void swap(int[] nums, int i, int j) {
C
CyC2018 已提交
149 150 151
    int t = nums[i];
    nums[i] = nums[j];
    nums[j] = t;
C
CyC2018 已提交
152 153 154
}
```

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

C
CyC2018 已提交
157 158
[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 已提交
159
## 题目描述
C
CyC2018 已提交
160 161 162 163

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

```html
C
CyC2018 已提交
164
Consider the following matrix:
C
CyC2018 已提交
165
[
C
CyC2018 已提交
166 167 168 169 170
  [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 已提交
171 172
]

C
CyC2018 已提交
173 174
Given target = 5, return true.
Given target = 20, return false.
C
CyC2018 已提交
175 176
```

C
CyC2018 已提交
177
## 解题思路
C
CyC2018 已提交
178

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

C
CyC2018 已提交
181 182
复杂度:O(M + N) + O(1)

C
CyC2018 已提交
183 184
当前元素的查找区间为左下角的所有元素,例如元素 12 的查找区间如下:

C
CyC2018 已提交
185
<div align="center"> <img src="../pics//f94389e9-55b1-4f49-9d37-00ed05900ae0.png" width="250"/> </div><br>
C
CyC2018 已提交
186 187

```java
C
CyC2018 已提交
188 189 190 191 192 193 194 195 196 197
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++;
C
CyC2018 已提交
198
        else
C
CyC2018 已提交
199 200 201
            c--;
    }
    return false;
C
CyC2018 已提交
202 203 204
}
```

C
CyC2018 已提交
205
# 5. 替换空格
C
CyC2018 已提交
206

C
CyC2018 已提交
207 208
[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 已提交
209
## 题目描述
C
CyC2018 已提交
210

C
CyC2018 已提交
211 212 213 214 215 216 217 218 219 220

将一个字符串中的空格替换成 "%20"。

```text
Input:
"We Are Happy"

Output:
"We%20Are%20Happy"
```
C
CyC2018 已提交
221

C
CyC2018 已提交
222
## 解题思路
C
CyC2018 已提交
223

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

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

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

```java
C
CyC2018 已提交
231
public String replaceSpace(StringBuffer str) {
C
CyC2018 已提交
232
    int P1 = str.length() - 1;
C
CyC2018 已提交
233
    for (int i = 0; i < P1 + 1; i++)
C
CyC2018 已提交
234
        if (str.charAt(i) == ' ')
C
CyC2018 已提交
235
            str.append("  ");
C
CyC2018 已提交
236

C
CyC2018 已提交
237
    int P2 = str.length() - 1;
C
CyC2018 已提交
238 239 240 241 242 243 244 245 246 247 248
    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 已提交
249 250 251
}
```

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

C
CyC2018 已提交
254 255
[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 已提交
256
## 题目描述
C
CyC2018 已提交
257 258 259

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

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

C
CyC2018 已提交
262
## 解题思路
C
CyC2018 已提交
263

C
CyC2018 已提交
264
### 使用栈
C
CyC2018 已提交
265 266

```java
C
CyC2018 已提交
267 268 269 270 271 272 273 274 275 276
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 已提交
277 278 279
}
```

C
CyC2018 已提交
280
### 使用递归
C
CyC2018 已提交
281 282

```java
C
CyC2018 已提交
283 284 285 286 287 288 289
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 已提交
290 291 292
}
```

C
CyC2018 已提交
293
### 使用头插法
C
CyC2018 已提交
294 295 296

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

C
CyC2018 已提交
297 298 299 300
头结点和第一个节点的区别:

- 头结点是在头插法中使用的一个额外节点,这个节点不存储值;
- 第一个节点就是链表的第一个真正存储值的节点。
C
CyC2018 已提交
301 302

```java
C
CyC2018 已提交
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
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 已提交
320 321 322
}
```

C
CyC2018 已提交
323 324 325 326 327 328 329 330 331 332 333 334 335 336
### 使用 Collections.reverse()

```java
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 已提交
337
# 7. 重建二叉树
C
CyC2018 已提交
338

C
CyC2018 已提交
339 340
[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 已提交
341
## 题目描述
C
CyC2018 已提交
342

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

```html
C
CyC2018 已提交
346 347
preorder = [3,9,20,15,7]
inorder =  [9,3,15,20,7]
C
CyC2018 已提交
348 349
```

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

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

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

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

C
CyC2018 已提交
360 361
public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
    for (int i = 0; i < in.length; i++)
C
CyC2018 已提交
362 363
        indexForInOrders.put(in[i], i);
    return reConstructBinaryTree(pre, 0, pre.length - 1, 0);
C
CyC2018 已提交
364 365
}

C
CyC2018 已提交
366
private TreeNode reConstructBinaryTree(int[] pre, int preL, int preR, int inL) {
C
CyC2018 已提交
367 368 369
    if (preL > preR)
        return null;
    TreeNode root = new TreeNode(pre[preL]);
C
CyC2018 已提交
370
    int inIndex = indexForInOrders.get(root.val);
C
CyC2018 已提交
371
    int leftTreeSize = inIndex - inL;
C
CyC2018 已提交
372 373
    root.left = reConstructBinaryTree(pre, preL + 1, preL + leftTreeSize, inL);
    root.right = reConstructBinaryTree(pre, preL + leftTreeSize + 1, preR, inL + leftTreeSize + 1);
C
CyC2018 已提交
374
    return root;
C
CyC2018 已提交
375 376 377
}
```

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

C
CyC2018 已提交
380 381
[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 已提交
382
## 题目描述
C
CyC2018 已提交
383 384 385 386

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

```java
C
CyC2018 已提交
387 388 389 390 391
public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;
C
CyC2018 已提交
392

C
CyC2018 已提交
393 394 395
    TreeLinkNode(int val) {
        this.val = val;
    }
C
CyC2018 已提交
396 397 398
}
```

C
CyC2018 已提交
399 400 401 402 403 404 405 406 407 408
## 解题思路

① 如果一个节点的右子树不为空,那么该节点的下一个节点是右子树的最左节点;

<div align="center"> <img src="../pics//cb0ed469-27ab-471b-a830-648b279103c8.png" width="250"/> </div><br>

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

<div align="center"> <img src="../pics//e143f6da-d114-4ba4-8712-f65299047fa2.png" width="250"/> </div><br>

C
CyC2018 已提交
409
```java
C
CyC2018 已提交
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
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 已提交
425 426 427
}
```

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

C
CyC2018 已提交
430 431
[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 已提交
432
## 题目描述
C
CyC2018 已提交
433

C
CyC2018 已提交
434
用两个栈来实现一个队列,完成队列的 Push 和 Pop 操作。
C
CyC2018 已提交
435

C
CyC2018 已提交
436
## 解题思路
C
CyC2018 已提交
437

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

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

```java
C
CyC2018 已提交
443 444
Stack<Integer> in = new Stack<Integer>();
Stack<Integer> out = new Stack<Integer>();
C
CyC2018 已提交
445

C
CyC2018 已提交
446 447
public void push(int node) {
    in.push(node);
C
CyC2018 已提交
448 449
}

C
CyC2018 已提交
450 451 452 453
public int pop() throws Exception {
    if (out.isEmpty())
        while (!in.isEmpty())
            out.push(in.pop());
C
CyC2018 已提交
454

C
CyC2018 已提交
455 456
    if (out.isEmpty())
        throw new Exception("queue is empty");
C
CyC2018 已提交
457

C
CyC2018 已提交
458
    return out.pop();
C
CyC2018 已提交
459 460 461
}
```

C
CyC2018 已提交
462
# 10.1 斐波那契数列
C
CyC2018 已提交
463

C
CyC2018 已提交
464 465
[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 已提交
466
## 题目描述
C
CyC2018 已提交
467

C
CyC2018 已提交
468
求斐波那契数列的第 n 项,n <= 39。
C
CyC2018 已提交
469

C
CyC2018 已提交
470
<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 已提交
471

C
CyC2018 已提交
472
## 解题思路
C
CyC2018 已提交
473

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

C
CyC2018 已提交
476
<div align="center"> <img src="../pics//faecea49-9974-40db-9821-c8636137df61.jpg" width="300"/> </div><br>
C
CyC2018 已提交
477

C
CyC2018 已提交
478
递归是将一个问题划分成多个子问题求解,动态规划也是如此,但是动态规划会把子问题的解缓存起来,从而避免重复求解子问题。
C
CyC2018 已提交
479

C
CyC2018 已提交
480
```java
C
CyC2018 已提交
481 482 483 484 485 486 487 488
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 已提交
489 490 491
}
```

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

```java
C
CyC2018 已提交
495 496 497 498 499 500 501 502 503 504 505
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 已提交
506 507 508
}
```

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

C
CyC2018 已提交
511
```java
C
CyC2018 已提交
512 513
public class Solution {
    private int[] fib = new int[40];
C
CyC2018 已提交
514

C
CyC2018 已提交
515 516 517 518 519 520
    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 已提交
521

C
CyC2018 已提交
522 523 524
    public int Fibonacci(int n) {
        return fib[n];
    }
C
CyC2018 已提交
525 526 527
}
```

C
CyC2018 已提交
528
# 10.2 跳台阶
C
CyC2018 已提交
529

C
CyC2018 已提交
530 531
[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 已提交
532
## 题目描述
C
CyC2018 已提交
533

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

C
CyC2018 已提交
536
## 解题思路
C
CyC2018 已提交
537

C
CyC2018 已提交
538
```java
C
CyC2018 已提交
539
public int JumpFloor(int n) {
C
CyC2018 已提交
540
    if (n <= 2)
C
CyC2018 已提交
541
        return n;
C
CyC2018 已提交
542 543 544
    int pre2 = 1, pre1 = 2;
    int result = 1;
    for (int i = 2; i < n; i++) {
C
CyC2018 已提交
545 546 547 548 549
        result = pre2 + pre1;
        pre2 = pre1;
        pre1 = result;
    }
    return result;
C
CyC2018 已提交
550 551 552
}
```

C
CyC2018 已提交
553
# 10.3 矩形覆盖
C
CyC2018 已提交
554

C
CyC2018 已提交
555 556
[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 已提交
557
## 题目描述
C
CyC2018 已提交
558

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

C
CyC2018 已提交
561
## 解题思路
C
CyC2018 已提交
562

C
CyC2018 已提交
563
```java
C
CyC2018 已提交
564 565 566 567 568 569 570 571 572 573 574
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 已提交
575 576 577
}
```

C
CyC2018 已提交
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
# 10.4 变态跳台阶

[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)

## 题目描述

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

## 解题思路

```java
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 已提交
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 607
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。

C
CyC2018 已提交
608
例如数组 {3, 4, 5, 1, 2} 为 {1, 2, 3, 4, 5} 的一个旋转,该数组的最小值为 1。
C
CyC2018 已提交
609

C
CyC2018 已提交
610
## 解题思路
C
CyC2018 已提交
611

C
CyC2018 已提交
612 613 614 615
在一个有序数组中查找一个元素可以用二分查找,二分查找也称为折半查找,每次都能将查找区间减半,这种折半特性的算法时间复杂度都为 O(logN)。

本题可以修改二分查找算法进行求解:

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

C
CyC2018 已提交
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
```java
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 已提交
634

C
CyC2018 已提交
635
如果数组元素允许重复的话,那么就会出现一个特殊的情况:nums[l] == nums[m] == nums[h],那么此时无法确定解在哪个区间,需要切换到顺序查找。例如对于数组 {1,1,1,0,1},l、m 和 h 指向的数都为 1,此时无法知道最小数字 0 在哪个区间。
C
CyC2018 已提交
636 637

```java
C
CyC2018 已提交
638 639 640 641 642 643
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;
C
CyC2018 已提交
644 645 646
        if (nums[l] == nums[m] && nums[m] == nums[h])
            return minNumber(nums, l, h);
        else if (nums[m] <= nums[h])
C
CyC2018 已提交
647 648 649 650 651
            h = m;
        else
            l = m + 1;
    }
    return nums[l];
C
CyC2018 已提交
652
}
C
CyC2018 已提交
653 654 655 656 657 658 659

private int minNumber(int[] nums, int l, int h) {
    for (int i = l; i < h; i++)
        if (nums[i] > nums[i + 1])
            return nums[i + 1];
    return nums[l];
}
C
CyC2018 已提交
660 661
```

C
CyC2018 已提交
662
# 12. 矩阵中的路径
C
CyC2018 已提交
663

C
CyC2018 已提交
664 665
[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 已提交
666
## 题目描述
C
CyC2018 已提交
667 668 669

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

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

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

C
CyC2018 已提交
674
## 解题思路
C
CyC2018 已提交
675 676

```java
C
CyC2018 已提交
677 678 679
private final static int[][] next = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
private int rows;
private int cols;
C
CyC2018 已提交
680

C
CyC2018 已提交
681 682 683 684 685 686 687 688 689 690 691 692
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 已提交
693 694
}

C
CyC2018 已提交
695 696 697 698 699 700 701 702 703 704 705
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 已提交
706 707
}

C
CyC2018 已提交
708 709 710 711 712 713
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 已提交
714 715 716
}
```

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

C
CyC2018 已提交
719 720
[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 已提交
721
## 题目描述
C
CyC2018 已提交
722

C
CyC2018 已提交
723 724 725
地上有一个 m 行和 n 列的方格。一个机器人从坐标 (0, 0) 的格子开始移动,每一次只能向左右上下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于 k 的格子。

例如,当 k 为 18 时,机器人能够进入方格 (35,37),因为 3+5+3+7=18。但是,它不能进入方格 (35,37),因为 3+5+3+8=19。请问该机器人能够达到多少个格子?
C
CyC2018 已提交
726

C
CyC2018 已提交
727
## 解题思路
C
CyC2018 已提交
728 729

```java
C
CyC2018 已提交
730 731 732 733 734 735
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 已提交
736

C
CyC2018 已提交
737 738 739 740 741 742 743 744
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 已提交
745 746
}

C
CyC2018 已提交
747 748 749 750 751 752 753 754 755
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 已提交
756 757
}

C
CyC2018 已提交
758 759 760 761 762 763 764 765 766
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;
        }
    }
C
CyC2018 已提交
767
    this.digitSum = new int[rows][cols];
C
CyC2018 已提交
768 769
    for (int i = 0; i < this.rows; i++)
        for (int j = 0; j < this.cols; j++)
C
CyC2018 已提交
770
            this.digitSum[i][j] = digitSumOne[i] + digitSumOne[j];
C
CyC2018 已提交
771 772 773
}
```

C
CyC2018 已提交
774
# 14. 剪绳子
C
CyC2018 已提交
775

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

C
CyC2018 已提交
778
## 题目描述
C
CyC2018 已提交
779 780 781

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

C
CyC2018 已提交
782 783 784
```html
n = 2
return 1 (2 = 1 + 1)
C
CyC2018 已提交
785

C
CyC2018 已提交
786 787
n = 10
return 36 (10 = 3 + 3 + 4)
C
CyC2018 已提交
788 789
```

C
CyC2018 已提交
790 791 792
## 解题思路

### 贪心
C
CyC2018 已提交
793

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

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

```java
C
CyC2018 已提交
799 800 801 802 803 804 805 806 807 808 809 810
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 已提交
811 812 813
}
```

C
CyC2018 已提交
814 815 816 817 818 819 820 821 822 823 824 825 826
### 动态规划

```java
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 已提交
827
# 15. 二进制中 1 的个数
C
CyC2018 已提交
828

C
CyC2018 已提交
829 830
[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 已提交
831
## 题目描述
C
CyC2018 已提交
832

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

C
CyC2018 已提交
835
### n&(n-1)
C
CyC2018 已提交
836

C
CyC2018 已提交
837
该位运算去除 n 的位级表示中最低的那一位。
C
CyC2018 已提交
838 839

```
C
CyC2018 已提交
840 841 842
n       : 10110100
n-1     : 10110011
n&(n-1) : 10110000
C
CyC2018 已提交
843 844
```

C
CyC2018 已提交
845 846 847
时间复杂度:O(M),其中 M 表示 1 的个数。


C
CyC2018 已提交
848
```java
C
CyC2018 已提交
849 850 851 852 853 854 855
public int NumberOf1(int n) {
    int cnt = 0;
    while (n != 0) {
        cnt++;
        n &= (n - 1);
    }
    return cnt;
C
CyC2018 已提交
856 857 858
}
```

C
CyC2018 已提交
859 860 861 862 863 864 865 866 867

### Integer.bitCount()

```java
public int NumberOf1(int n) {
    return Integer.bitCount(n);
}
```

C
CyC2018 已提交
868
# 16. 数值的整数次方
C
CyC2018 已提交
869

C
CyC2018 已提交
870 871
[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 已提交
872
## 题目描述
C
CyC2018 已提交
873

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

C
CyC2018 已提交
876
## 解题思路
C
CyC2018 已提交
877

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

C
CyC2018 已提交
880
<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 已提交
881

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

```java
C
CyC2018 已提交
885 886 887 888 889 890 891 892 893 894 895 896 897 898
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 已提交
899 900 901
}
```

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

C
CyC2018 已提交
904
## 题目描述
C
CyC2018 已提交
905

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

C
CyC2018 已提交
908
## 解题思路
C
CyC2018 已提交
909

C
CyC2018 已提交
910
由于 n 可能会非常大,因此不能直接用 int 表示数字,而是用 char 数组进行存储。
C
CyC2018 已提交
911 912 913 914

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

```java
C
CyC2018 已提交
915 916 917 918
public void print1ToMaxOfNDigits(int n) {
    if (n <= 0)
        return;
    char[] number = new char[n];
C
CyC2018 已提交
919
    print1ToMaxOfNDigits(number, 0);
C
CyC2018 已提交
920 921
}

C
CyC2018 已提交
922
private void print1ToMaxOfNDigits(char[] number, int digit) {
C
CyC2018 已提交
923
    if (digit == number.length) {
C
CyC2018 已提交
924 925 926 927
        printNumber(number);
        return;
    }
    for (int i = 0; i < 10; i++) {
C
CyC2018 已提交
928
        number[digit] = (char) (i + '0');
C
CyC2018 已提交
929 930
        print1ToMaxOfNDigits(number, digit + 1);
    }
C
CyC2018 已提交
931 932
}

C
CyC2018 已提交
933 934 935 936 937 938 939
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 已提交
940 941 942
}
```

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

C
CyC2018 已提交
945
## 解题思路
C
CyC2018 已提交
946

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

C
CyC2018 已提交
949
<div align="center"> <img src="../pics//27ff9548-edb6-4465-92c8-7e6386e0b185.png" width="600"/> </div><br>
C
CyC2018 已提交
950

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

C
CyC2018 已提交
953
<div align="center"> <img src="../pics//280f7728-594f-4811-a03a-fa8d32c013da.png" width="600"/> </div><br>
C
CyC2018 已提交
954

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

```java
C
CyC2018 已提交
958 959 960 961 962 963 964 965 966 967 968 969 970 971 972
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 已提交
973 974 975
}
```

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

C
CyC2018 已提交
978 979
[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 已提交
980
## 题目描述
C
CyC2018 已提交
981

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

C
CyC2018 已提交
984
## 解题描述
C
CyC2018 已提交
985 986

```java
C
CyC2018 已提交
987 988 989 990 991 992 993 994 995 996 997 998
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 已提交
999 1000 1001
}
```

C
CyC2018 已提交
1002
# 19. 正则表达式匹配
C
CyC2018 已提交
1003

C
CyC2018 已提交
1004 1005
[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 已提交
1006
## 题目描述
C
CyC2018 已提交
1007

C
CyC2018 已提交
1008 1009 1010
请实现一个函数用来匹配包括 '.' 和 '\*' 的正则表达式。模式中的字符 '.' 表示任意一个字符,而 '\*' 表示它前面的字符可以出现任意次(包含 0 次)。

在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串 "aaa" 与模式 "a.a" 和 "ab\*ac\*a" 匹配,但是与 "aa.a" 和 "ab\*a" 均不匹配。
C
CyC2018 已提交
1011

C
CyC2018 已提交
1012
## 解题思路
C
CyC2018 已提交
1013

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

```java
C
CyC2018 已提交
1017
public boolean match(char[] str, char[] pattern) {
C
CyC2018 已提交
1018

C
CyC2018 已提交
1019 1020
    int m = str.length, n = pattern.length;
    boolean[][] dp = new boolean[m + 1][n + 1];
C
CyC2018 已提交
1021

C
CyC2018 已提交
1022 1023 1024 1025
    dp[0][0] = true;
    for (int i = 1; i <= n; i++)
        if (pattern[i - 1] == '*')
            dp[0][i] = dp[0][i - 2];
C
CyC2018 已提交
1026

C
CyC2018 已提交
1027 1028 1029 1030 1031
    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] == '*')
C
CyC2018 已提交
1032 1033 1034 1035 1036 1037 1038
                if (pattern[j - 2] == str[i - 1] || pattern[j - 2] == '.') {
                    dp[i][j] |= dp[i][j - 1]; // a* counts as single a
                    dp[i][j] |= dp[i - 1][j]; // a* counts as multiple a
                    dp[i][j] |= dp[i][j - 2]; // a* counts as empty
                } else
                    dp[i][j] = dp[i][j - 2];   // a* only counts as empty

C
CyC2018 已提交
1039
    return dp[m][n];
C
CyC2018 已提交
1040 1041 1042
}
```

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

C
CyC2018 已提交
1045 1046
[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 已提交
1047
## 题目描述
C
CyC2018 已提交
1048

C
CyC2018 已提交
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065
```html
true

"+100"
"5e2"
"-123"
"3.1416"
"-1E-16"

false

"12e"
"1a3.14"
"1.2.3"
"+-5"
"12e+4.3"
```
C
CyC2018 已提交
1066

C
CyC2018 已提交
1067

C
CyC2018 已提交
1068
## 解题思路
C
CyC2018 已提交
1069

C
CyC2018 已提交
1070 1071 1072 1073
使用正则表达式进行匹配。

```html
[]  : 字符集合
C
CyC2018 已提交
1074
()  : 分组
C
CyC2018 已提交
1075 1076 1077 1078 1079
?   : 重复 0 ~ 1
+   : 重复 1 ~ n
*   : 重复 0 ~ n
.   : 任意字符
\\. : 转义后的 .
C
CyC2018 已提交
1080
\\d : 数字
C
CyC2018 已提交
1081 1082
```

C
CyC2018 已提交
1083
```java
C
CyC2018 已提交
1084
public boolean isNumeric(char[] str) {
C
CyC2018 已提交
1085
    if (str == null || str.length == 0)
C
CyC2018 已提交
1086 1087
        return false;
    return new String(str).matches("[+-]?\\d*(\\.\\d+)?([eE][+-]?\\d+)?");
C
CyC2018 已提交
1088 1089 1090
}
```

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

C
CyC2018 已提交
1093 1094
[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 已提交
1095
## 题目描述
C
CyC2018 已提交
1096

C
CyC2018 已提交
1097
需要保证奇数和奇数,偶数和偶数之间的相对位置不变,这和书本不太一样。
C
CyC2018 已提交
1098

C
CyC2018 已提交
1099
## 解题思路
C
CyC2018 已提交
1100 1101

```java
C
CyC2018 已提交
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115
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 已提交
1116 1117 1118
}
```

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

C
CyC2018 已提交
1121 1122
[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 已提交
1123
## 解题思路
C
CyC2018 已提交
1124

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

C
CyC2018 已提交
1127
<div align="center"> <img src="../pics//ea2304ce-268b-4238-9486-4d8f8aea8ca4.png" width="500"/> </div><br>
C
CyC2018 已提交
1128 1129

```java
C
CyC2018 已提交
1130
public ListNode FindKthToTail(ListNode head, int k) {
C
CyC2018 已提交
1131 1132
    if (head == null)
        return null;
C
CyC2018 已提交
1133 1134 1135
    ListNode P1 = head;
    while (P1 != null && k-- > 0)
        P1 = P1.next;
C
CyC2018 已提交
1136 1137
    if (k > 0)
        return null;
C
CyC2018 已提交
1138 1139 1140 1141
    ListNode P2 = head;
    while (P1 != null) {
        P1 = P1.next;
        P2 = P2.next;
C
CyC2018 已提交
1142
    }
C
CyC2018 已提交
1143
    return P2;
C
CyC2018 已提交
1144 1145 1146
}
```

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

C
CyC2018 已提交
1149 1150
[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 已提交
1151 1152
## 题目描述

C
CyC2018 已提交
1153
一个链表中包含环,请找出该链表的环的入口结点。要求不能使用额外的空间。
C
CyC2018 已提交
1154

C
CyC2018 已提交
1155
## 解题思路
C
CyC2018 已提交
1156

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

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

C
CyC2018 已提交
1161
<div align="center"> <img src="../pics//70fa1f83-dae7-456d-b94b-ce28963b2ba1.png"/> </div><br>
C
CyC2018 已提交
1162 1163

```java
C
CyC2018 已提交
1164
public ListNode EntryNodeOfLoop(ListNode pHead) {
C
CyC2018 已提交
1165 1166 1167
    if (pHead == null || pHead.next == null)
        return null;
    ListNode slow = pHead, fast = pHead;
C
CyC2018 已提交
1168
    do {
C
CyC2018 已提交
1169 1170
        fast = fast.next.next;
        slow = slow.next;
C
CyC2018 已提交
1171
    } while (slow != fast);
C
CyC2018 已提交
1172 1173 1174 1175 1176 1177
    fast = pHead;
    while (slow != fast) {
        slow = slow.next;
        fast = fast.next;
    }
    return slow;
C
CyC2018 已提交
1178 1179 1180
}
```

C
CyC2018 已提交
1181
# 24. 反转链表
C
CyC2018 已提交
1182

C
CyC2018 已提交
1183 1184
[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 已提交
1185
## 解题思路
C
CyC2018 已提交
1186

C
CyC2018 已提交
1187
### 递归
C
CyC2018 已提交
1188 1189

```java
C
CyC2018 已提交
1190 1191 1192 1193 1194 1195 1196 1197
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 已提交
1198 1199 1200
}
```

C
CyC2018 已提交
1201
### 迭代
C
CyC2018 已提交
1202 1203

```java
C
CyC2018 已提交
1204 1205 1206 1207 1208 1209 1210 1211 1212
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 已提交
1213 1214 1215
}
```

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

C
CyC2018 已提交
1218 1219
[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 已提交
1220
## 题目描述
C
CyC2018 已提交
1221

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

C
CyC2018 已提交
1224
## 解题思路
C
CyC2018 已提交
1225

C
CyC2018 已提交
1226
### 递归
C
CyC2018 已提交
1227 1228

```java
C
CyC2018 已提交
1229
public ListNode Merge(ListNode list1, ListNode list2) {
C
CyC2018 已提交
1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240
    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 已提交
1241 1242 1243
}
```

C
CyC2018 已提交
1244
### 迭代
C
CyC2018 已提交
1245 1246

```java
C
CyC2018 已提交
1247
public ListNode Merge(ListNode list1, ListNode list2) {
C
CyC2018 已提交
1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264
    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 已提交
1265 1266 1267
}
```

C
CyC2018 已提交
1268
# 26. 树的子结构
C
CyC2018 已提交
1269

C
CyC2018 已提交
1270 1271
[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 已提交
1272
## 题目描述
C
CyC2018 已提交
1273

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

C
CyC2018 已提交
1276
## 解题思路
C
CyC2018 已提交
1277 1278

```java
C
CyC2018 已提交
1279
public boolean HasSubtree(TreeNode root1, TreeNode root2) {
C
CyC2018 已提交
1280 1281
    if (root1 == null || root2 == null)
        return false;
C
CyC2018 已提交
1282
    return isSubtreeWithRoot(root1, root2) || HasSubtree(root1.left, root2) || HasSubtree(root1.right, root2);
C
CyC2018 已提交
1283 1284
}

C
CyC2018 已提交
1285
private boolean isSubtreeWithRoot(TreeNode root1, TreeNode root2) {
C
CyC2018 已提交
1286 1287 1288 1289 1290 1291
    if (root2 == null)
        return true;
    if (root1 == null)
        return false;
    if (root1.val != root2.val)
        return false;
C
CyC2018 已提交
1292
    return isSubtreeWithRoot(root1.left, root2.left) && isSubtreeWithRoot(root1.right, root2.right);
C
CyC2018 已提交
1293 1294 1295
}
```

C
CyC2018 已提交
1296
# 27. 二叉树的镜像
C
CyC2018 已提交
1297

C
CyC2018 已提交
1298 1299
[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 已提交
1300
## 题目描述
C
CyC2018 已提交
1301

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

C
CyC2018 已提交
1304
## 解题思路
C
CyC2018 已提交
1305 1306

```java
C
CyC2018 已提交
1307
public void Mirror(TreeNode root) {
C
CyC2018 已提交
1308 1309 1310 1311 1312
    if (root == null)
        return;
    swap(root);
    Mirror(root.left);
    Mirror(root.right);
C
CyC2018 已提交
1313 1314
}

C
CyC2018 已提交
1315
private void swap(TreeNode root) {
C
CyC2018 已提交
1316 1317 1318
    TreeNode t = root.left;
    root.left = root.right;
    root.right = t;
C
CyC2018 已提交
1319 1320 1321
}
```

C
CyC2018 已提交
1322
# 28 对称的二叉树
C
CyC2018 已提交
1323

C
CyC2018 已提交
1324 1325
[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 已提交
1326
## 题目描述
C
CyC2018 已提交
1327

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

C
CyC2018 已提交
1330
## 解题思路
C
CyC2018 已提交
1331 1332

```java
C
CyC2018 已提交
1333
boolean isSymmetrical(TreeNode pRoot) {
C
CyC2018 已提交
1334 1335 1336
    if (pRoot == null)
        return true;
    return isSymmetrical(pRoot.left, pRoot.right);
C
CyC2018 已提交
1337 1338
}

C
CyC2018 已提交
1339
boolean isSymmetrical(TreeNode t1, TreeNode t2) {
C
CyC2018 已提交
1340 1341 1342 1343 1344 1345 1346
    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 已提交
1347 1348 1349
}
```

C
CyC2018 已提交
1350
# 29. 顺时针打印矩阵
C
CyC2018 已提交
1351

C
CyC2018 已提交
1352 1353
[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 已提交
1354
## 题目描述
C
CyC2018 已提交
1355

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

C
CyC2018 已提交
1358
<div align="center"> <img src="../pics//6539b9a4-2b24-4d10-8c94-2eb5aba1e296.png" width="300"/> </div><br>
C
CyC2018 已提交
1359

C
CyC2018 已提交
1360
## 解题思路
C
CyC2018 已提交
1361 1362

```java
C
CyC2018 已提交
1363
public ArrayList<Integer> printMatrix(int[][] matrix) {
C
CyC2018 已提交
1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379
    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 已提交
1380 1381 1382
}
```

C
CyC2018 已提交
1383
# 30. 包含 min 函数的栈
C
CyC2018 已提交
1384

C
CyC2018 已提交
1385 1386
[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 已提交
1387
## 题目描述
C
CyC2018 已提交
1388

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

C
CyC2018 已提交
1391
## 解题思路
C
CyC2018 已提交
1392 1393

```java
C
CyC2018 已提交
1394
private Stack<Integer> dataStack = new Stack<>();
C
CyC2018 已提交
1395
private Stack<Integer> minStack = new Stack<>();
C
CyC2018 已提交
1396

C
CyC2018 已提交
1397
public void push(int node) {
C
CyC2018 已提交
1398
    dataStack.push(node);
C
CyC2018 已提交
1399
    minStack.push(minStack.isEmpty() ? node : Math.min(minStack.peek(), node));
C
CyC2018 已提交
1400 1401
}

C
CyC2018 已提交
1402
public void pop() {
C
CyC2018 已提交
1403
    dataStack.pop();
C
CyC2018 已提交
1404
    minStack.pop();
C
CyC2018 已提交
1405 1406
}

C
CyC2018 已提交
1407
public int top() {
C
CyC2018 已提交
1408
    return dataStack.peek();
C
CyC2018 已提交
1409 1410
}

C
CyC2018 已提交
1411
public int min() {
C
CyC2018 已提交
1412
    return minStack.peek();
C
CyC2018 已提交
1413 1414 1415
}
```

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

C
CyC2018 已提交
1418 1419
[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 已提交
1420
## 题目描述
C
CyC2018 已提交
1421

C
CyC2018 已提交
1422 1423 1424
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。

例如序列 1,2,3,4,5 是某栈的压入顺序,序列 4,5,3,2,1 是该压栈序列对应的一个弹出序列,但 4,3,5,1,2 就不可能是该压栈序列的弹出序列。
C
CyC2018 已提交
1425

C
CyC2018 已提交
1426
## 解题思路
C
CyC2018 已提交
1427 1428 1429 1430

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

```java
C
CyC2018 已提交
1431
public boolean IsPopOrder(int[] pushSequence, int[] popSequence) {
C
CyC2018 已提交
1432
    int n = pushSequence.length;
C
CyC2018 已提交
1433 1434
    Stack<Integer> stack = new Stack<>();
    for (int pushIndex = 0, popIndex = 0; pushIndex < n; pushIndex++) {
C
CyC2018 已提交
1435
        stack.push(pushSequence[pushIndex]);
C
CyC2018 已提交
1436 1437
        while (popIndex < n && !stack.isEmpty() 
                && stack.peek() == popSequence[popIndex]) {
C
CyC2018 已提交
1438 1439 1440 1441 1442
            stack.pop();
            popIndex++;
        }
    }
    return stack.isEmpty();
C
CyC2018 已提交
1443 1444 1445
}
```

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

C
CyC2018 已提交
1448 1449
[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 已提交
1450
## 题目描述
C
CyC2018 已提交
1451 1452 1453 1454 1455

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

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

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

C
CyC2018 已提交
1458
## 解题思路
C
CyC2018 已提交
1459 1460 1461 1462 1463 1464

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

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

```java
C
CyC2018 已提交
1465
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
C
CyC2018 已提交
1466 1467 1468 1469 1470 1471 1472
    Queue<TreeNode> queue = new LinkedList<>();
    ArrayList<Integer> ret = new ArrayList<>();
    queue.add(root);
    while (!queue.isEmpty()) {
        int cnt = queue.size();
        while (cnt-- > 0) {
            TreeNode t = queue.poll();
C
CyC2018 已提交
1473 1474
            if (t == null)
                continue;
C
CyC2018 已提交
1475
            ret.add(t.val);
C
CyC2018 已提交
1476 1477
            queue.add(t.left);
            queue.add(t.right);
C
CyC2018 已提交
1478 1479 1480
        }
    }
    return ret;
C
CyC2018 已提交
1481 1482 1483
}
```

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

C
CyC2018 已提交
1486 1487
[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 已提交
1488
## 题目描述
C
CyC2018 已提交
1489 1490 1491

和上题几乎一样。

C
CyC2018 已提交
1492
## 解题思路
C
CyC2018 已提交
1493 1494

```java
C
CyC2018 已提交
1495
ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
C
CyC2018 已提交
1496 1497 1498 1499 1500 1501 1502 1503
    ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
    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();
C
CyC2018 已提交
1504 1505
            if (node == null)
                continue;
C
CyC2018 已提交
1506
            list.add(node.val);
C
CyC2018 已提交
1507 1508
            queue.add(node.left);
            queue.add(node.right);
C
CyC2018 已提交
1509
        }
C
CyC2018 已提交
1510 1511
        if (list.size() != 0)
            ret.add(list);
C
CyC2018 已提交
1512 1513
    }
    return ret;
C
CyC2018 已提交
1514 1515 1516
}
```

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

C
CyC2018 已提交
1519 1520
[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 已提交
1521
## 题目描述
C
CyC2018 已提交
1522 1523 1524

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

C
CyC2018 已提交
1525 1526 1527
## 解题思路

```java
C
CyC2018 已提交
1528
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
C
CyC2018 已提交
1529 1530 1531 1532 1533 1534 1535 1536 1537
    ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
    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();
C
CyC2018 已提交
1538 1539
            if (node == null)
                continue;
C
CyC2018 已提交
1540
            list.add(node.val);
C
CyC2018 已提交
1541 1542
            queue.add(node.left);
            queue.add(node.right);
C
CyC2018 已提交
1543 1544 1545 1546
        }
        if (reverse)
            Collections.reverse(list);
        reverse = !reverse;
C
CyC2018 已提交
1547 1548
        if (list.size() != 0)
            ret.add(list);
C
CyC2018 已提交
1549 1550 1551 1552 1553 1554
    }
    return ret;
}
```

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

C
CyC2018 已提交
1556 1557
[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 已提交
1558
## 题目描述
C
CyC2018 已提交
1559

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

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

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

C
CyC2018 已提交
1566
## 解题思路
C
CyC2018 已提交
1567 1568

```java
C
CyC2018 已提交
1569
public boolean VerifySquenceOfBST(int[] sequence) {
C
CyC2018 已提交
1570 1571 1572
    if (sequence == null || sequence.length == 0)
        return false;
    return verify(sequence, 0, sequence.length - 1);
C
CyC2018 已提交
1573 1574
}

C
CyC2018 已提交
1575
private boolean verify(int[] sequence, int first, int last) {
C
CyC2018 已提交
1576 1577 1578 1579 1580 1581
    if (last - first <= 1)
        return true;
    int rootVal = sequence[last];
    int cutIndex = first;
    while (cutIndex < last && sequence[cutIndex] <= rootVal)
        cutIndex++;
C
CyC2018 已提交
1582
    for (int i = cutIndex; i < last; i++)
C
CyC2018 已提交
1583 1584 1585
        if (sequence[i] < rootVal)
            return false;
    return verify(sequence, first, cutIndex - 1) && verify(sequence, cutIndex, last - 1);
C
CyC2018 已提交
1586 1587 1588
}
```

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

C
CyC2018 已提交
1591 1592
[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 已提交
1593
## 题目描述
C
CyC2018 已提交
1594 1595 1596

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

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

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

C
CyC2018 已提交
1601
## 解题思路
C
CyC2018 已提交
1602 1603

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

C
CyC2018 已提交
1606
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {
C
CyC2018 已提交
1607 1608
    backtracking(root, target, new ArrayList<>());
    return ret;
C
CyC2018 已提交
1609 1610
}

C
CyC2018 已提交
1611
private void backtracking(TreeNode node, int target, ArrayList<Integer> path) {
C
CyC2018 已提交
1612 1613 1614 1615 1616
    if (node == null)
        return;
    path.add(node.val);
    target -= node.val;
    if (target == 0 && node.left == null && node.right == null) {
C
CyC2018 已提交
1617
        ret.add(new ArrayList<>(path));
C
CyC2018 已提交
1618 1619 1620 1621 1622
    } else {
        backtracking(node.left, target, path);
        backtracking(node.right, target, path);
    }
    path.remove(path.size() - 1);
C
CyC2018 已提交
1623 1624 1625
}
```

C
CyC2018 已提交
1626
# 35. 复杂链表的复制
C
CyC2018 已提交
1627

C
CyC2018 已提交
1628 1629
[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 已提交
1630
## 题目描述
C
CyC2018 已提交
1631

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

C
CyC2018 已提交
1634
```java
C
CyC2018 已提交
1635
public class RandomListNode {
C
CyC2018 已提交
1636 1637 1638 1639 1640 1641 1642 1643 1644 1645
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}
```

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

C
CyC2018 已提交
1648
## 解题思路
C
CyC2018 已提交
1649 1650 1651

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

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

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

C
CyC2018 已提交
1656
<div align="center"> <img src="../pics//323ffd6c-8b54-4f3e-b361-555a6c8bf218.png" width="600"/> </div><br>
C
CyC2018 已提交
1657 1658 1659

第三步,拆分。

C
CyC2018 已提交
1660 1661 1662
<div align="center"> <img src="../pics//8f3b9519-d705-48fe-87ad-2e4052fc81d2.png" width="600"/> </div><br>

```java
C
CyC2018 已提交
1663
public RandomListNode Clone(RandomListNode pHead) {
C
CyC2018 已提交
1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694
    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 已提交
1695

C
CyC2018 已提交
1696 1697
[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 已提交
1698
## 题目描述
C
CyC2018 已提交
1699 1700 1701

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

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

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

```java
C
CyC2018 已提交
1707 1708
private TreeNode pre = null;
private TreeNode head = null;
C
CyC2018 已提交
1709

C
CyC2018 已提交
1710
public TreeNode Convert(TreeNode root) {
C
CyC2018 已提交
1711 1712
    inOrder(root);
    return head;
C
CyC2018 已提交
1713 1714
}

C
CyC2018 已提交
1715
private void inOrder(TreeNode node) {
C
CyC2018 已提交
1716 1717 1718 1719 1720 1721 1722 1723 1724 1725
    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 已提交
1726 1727 1728
}
```

C
CyC2018 已提交
1729
# 37. 序列化二叉树
C
CyC2018 已提交
1730

C
CyC2018 已提交
1731 1732
[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 已提交
1733
## 题目描述
C
CyC2018 已提交
1734 1735 1736

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

C
CyC2018 已提交
1737
## 解题思路
C
CyC2018 已提交
1738 1739

```java
C
CyC2018 已提交
1740
private String deserializeStr;
C
CyC2018 已提交
1741

C
CyC2018 已提交
1742
public String Serialize(TreeNode root) {
C
CyC2018 已提交
1743 1744 1745 1746
    if (root == null)
        return "#";
    return root.val + " " + Serialize(root.left) + " " + Serialize(root.right);
}
C
CyC2018 已提交
1747

C
CyC2018 已提交
1748
public TreeNode Deserialize(String str) {
C
CyC2018 已提交
1749 1750 1751
    deserializeStr = str;
    return Deserialize();
}
C
CyC2018 已提交
1752

C
CyC2018 已提交
1753
private TreeNode Deserialize() {
C
CyC2018 已提交
1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765
    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 已提交
1766 1767 1768
}
```

C
CyC2018 已提交
1769
# 38. 字符串的排列
C
CyC2018 已提交
1770

C
CyC2018 已提交
1771 1772
[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 已提交
1773
## 题目描述
C
CyC2018 已提交
1774

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

C
CyC2018 已提交
1777
## 解题思路
C
CyC2018 已提交
1778 1779

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

C
CyC2018 已提交
1782
public ArrayList<String> Permutation(String str) {
C
CyC2018 已提交
1783 1784 1785 1786
    if (str.length() == 0)
        return ret;
    char[] chars = str.toCharArray();
    Arrays.sort(chars);
C
CyC2018 已提交
1787
    backtracking(chars, new boolean[chars.length], new StringBuilder());
C
CyC2018 已提交
1788
    return ret;
C
CyC2018 已提交
1789 1790
}

C
CyC2018 已提交
1791
private void backtracking(char[] chars, boolean[] hasUsed, StringBuilder s) {
C
CyC2018 已提交
1792 1793 1794 1795 1796 1797 1798
    if (s.length() == chars.length) {
        ret.add(s.toString());
        return;
    }
    for (int i = 0; i < chars.length; i++) {
        if (hasUsed[i])
            continue;
C
CyC2018 已提交
1799
        if (i != 0 && chars[i] == chars[i - 1] && !hasUsed[i - 1]) /* 保证不重复 */
C
CyC2018 已提交
1800 1801 1802 1803 1804 1805 1806
            continue;
        hasUsed[i] = true;
        s.append(chars[i]);
        backtracking(chars, hasUsed, s);
        s.deleteCharAt(s.length() - 1);
        hasUsed[i] = false;
    }
C
CyC2018 已提交
1807 1808 1809
}
```

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

C
CyC2018 已提交
1812 1813
[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 已提交
1814
## 解题思路
C
CyC2018 已提交
1815

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

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

```java
C
CyC2018 已提交
1821
public int MoreThanHalfNum_Solution(int[] nums) {
C
CyC2018 已提交
1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834
    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 已提交
1835 1836 1837
}
```

C
CyC2018 已提交
1838
# 40. 最小的 K 个数
C
CyC2018 已提交
1839

C
CyC2018 已提交
1840 1841
[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 已提交
1842
## 解题思路
C
CyC2018 已提交
1843

C
CyC2018 已提交
1844
### 快速选择
C
CyC2018 已提交
1845

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

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

```java
C
CyC2018 已提交
1852
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k) {
C
CyC2018 已提交
1853 1854 1855
    ArrayList<Integer> ret = new ArrayList<>();
    if (k > nums.length || k <= 0)
        return ret;
C
CyC2018 已提交
1856 1857
    findKthSmallest(nums, k - 1);
    /* findKthSmallest 会改变数组,使得前 k 个数都是最小的 k 个数 */
C
CyC2018 已提交
1858 1859 1860
    for (int i = 0; i < k; i++)
        ret.add(nums[i]);
    return ret;
C
CyC2018 已提交
1861
}
C
CyC2018 已提交
1862

C
CyC2018 已提交
1863
public void findKthSmallest(int[] nums, int k) {
C
CyC2018 已提交
1864 1865 1866 1867 1868 1869 1870 1871 1872 1873
    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;
    }
C
CyC2018 已提交
1874
}
C
CyC2018 已提交
1875

C
CyC2018 已提交
1876
private int partition(int[] nums, int l, int h) {
C
CyC2018 已提交
1877
    int p = nums[l];     /* 切分元素 */
C
CyC2018 已提交
1878 1879
    int i = l, j = h + 1;
    while (true) {
C
CyC2018 已提交
1880 1881
        while (i != h && nums[++i] < p) ;
        while (j != l && nums[--j] > p) ;
C
CyC2018 已提交
1882 1883 1884 1885 1886 1887
        if (i >= j)
            break;
        swap(nums, i, j);
    }
    swap(nums, l, j);
    return j;
C
CyC2018 已提交
1888
}
C
CyC2018 已提交
1889

C
CyC2018 已提交
1890
private void swap(int[] nums, int i, int j) {
C
CyC2018 已提交
1891 1892 1893
    int t = nums[i];
    nums[i] = nums[j];
    nums[j] = t;
C
CyC2018 已提交
1894
}
C
CyC2018 已提交
1895 1896
```

C
CyC2018 已提交
1897
### 大小为 K 的最小堆
C
CyC2018 已提交
1898

C
CyC2018 已提交
1899 1900
- 复杂度:O(NlogK) + O(K)
- 特别适合处理海量数据
C
CyC2018 已提交
1901 1902 1903

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

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

```java
C
CyC2018 已提交
1907
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k) {
C
CyC2018 已提交
1908 1909 1910 1911 1912 1913 1914 1915
    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();
    }
C
CyC2018 已提交
1916
    return new ArrayList<>(maxHeap);
C
CyC2018 已提交
1917 1918 1919
}
```

C
CyC2018 已提交
1920
# 41.1 数据流中的中位数
C
CyC2018 已提交
1921

C
CyC2018 已提交
1922 1923
[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 已提交
1924
## 题目描述
C
CyC2018 已提交
1925 1926 1927

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

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

```java
C
CyC2018 已提交
1931 1932 1933 1934 1935 1936 1937
/* 大顶堆,存储左半边元素 */
private PriorityQueue<Integer> left = new PriorityQueue<>((o1, o2) -> o2 - o1);
/* 小顶堆,存储右半边元素,并且右半边元素都大于左半边 */
private PriorityQueue<Integer> right = new PriorityQueue<>();
/* 当前数据流读入的元素个数 */
private int N = 0;

C
CyC2018 已提交
1938
public void Insert(Integer val) {
C
CyC2018 已提交
1939 1940 1941 1942 1943 1944 1945 1946 1947 1948
    /* 插入要保证两个堆存于平衡状态 */
    if (N % 2 == 0) {
        /* N 为偶数的情况下插入到右半边。
         * 因为右半边元素都要大于左半边,但是新插入的元素不一定比左半边元素来的大,
         * 因此需要先将元素插入左半边,然后利用左半边为大顶堆的特点,取出堆顶元素即为最大元素,此时插入右半边 */
        left.add(val);
        right.add(left.poll());
    } else {
        right.add(val);
        left.add(right.poll());
C
CyC2018 已提交
1949
    }
C
CyC2018 已提交
1950 1951
    N++;
}
C
CyC2018 已提交
1952

C
CyC2018 已提交
1953
public Double GetMedian() {
C
CyC2018 已提交
1954 1955 1956 1957
    if (N % 2 == 0)
        return (left.peek() + right.peek()) / 2.0;
    else
        return (double) right.peek();
C
CyC2018 已提交
1958 1959 1960
}
```

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

C
CyC2018 已提交
1963 1964
[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 已提交
1965
## 题目描述
C
CyC2018 已提交
1966

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

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

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

C
CyC2018 已提交
1975
public void Insert(char ch) {
C
CyC2018 已提交
1976 1977 1978 1979
    cnts[ch]++;
    queue.add(ch);
    while (!queue.isEmpty() && cnts[queue.peek()] > 1)
        queue.poll();
C
CyC2018 已提交
1980
}
C
CyC2018 已提交
1981

C
CyC2018 已提交
1982
public char FirstAppearingOnce() {
C
CyC2018 已提交
1983
    return queue.isEmpty() ? '#' : queue.peek();
C
CyC2018 已提交
1984 1985 1986
}
```

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

C
CyC2018 已提交
1989 1990
[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 已提交
1991
## 题目描述
C
CyC2018 已提交
1992

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

C
CyC2018 已提交
1995
## 解题思路
C
CyC2018 已提交
1996 1997

```java
C
CyC2018 已提交
1998
public int FindGreatestSumOfSubArray(int[] nums) {
C
CyC2018 已提交
1999 2000
    if (nums == null || nums.length == 0)
        return 0;
C
CyC2018 已提交
2001
    int greatestSum = Integer.MIN_VALUE;
C
CyC2018 已提交
2002 2003 2004
    int sum = 0;
    for (int val : nums) {
        sum = sum <= 0 ? val : sum + val;
C
CyC2018 已提交
2005
        greatestSum = Math.max(greatestSum, sum);
C
CyC2018 已提交
2006
    }
C
CyC2018 已提交
2007
    return greatestSum;
C
CyC2018 已提交
2008
}
C
CyC2018 已提交
2009 2010
```

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

C
CyC2018 已提交
2013
[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 已提交
2014

C
CyC2018 已提交
2015
## 解题思路
C
CyC2018 已提交
2016 2017

```java
C
CyC2018 已提交
2018
public int NumberOf1Between1AndN_Solution(int n) {
C
CyC2018 已提交
2019 2020 2021 2022 2023 2024
    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 已提交
2025 2026 2027
}
```

C
CyC2018 已提交
2028
> [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 已提交
2029

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

C
CyC2018 已提交
2032
## 题目描述
C
CyC2018 已提交
2033

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

C
CyC2018 已提交
2036
## 解题思路
C
CyC2018 已提交
2037 2038

```java
C
CyC2018 已提交
2039
public int getDigitAtIndex(int index) {
C
CyC2018 已提交
2040 2041
    if (index < 0)
        return -1;
C
CyC2018 已提交
2042
    int place = 1;  // 1 表示个位,2 表示 十位...
C
CyC2018 已提交
2043
    while (true) {
C
CyC2018 已提交
2044 2045
        int amount = getAmountOfPlace(place);
        int totalAmount = amount * place;
C
CyC2018 已提交
2046
        if (index < totalAmount)
C
CyC2018 已提交
2047
            return getDigitAtIndex(index, place);
C
CyC2018 已提交
2048
        index -= totalAmount;
C
CyC2018 已提交
2049
        place++;
C
CyC2018 已提交
2050
    }
C
CyC2018 已提交
2051 2052 2053
}

/**
C
CyC2018 已提交
2054 2055
 * place 位数的数字组成的字符串长度
 * 10, 90, 900, ...
C
CyC2018 已提交
2056
 */
C
CyC2018 已提交
2057
private int getAmountOfPlace(int place) {
C
CyC2018 已提交
2058
    if (place == 1)
C
CyC2018 已提交
2059
        return 10;
C
CyC2018 已提交
2060
    return (int) Math.pow(10, place - 1) * 9;
C
CyC2018 已提交
2061 2062 2063
}

/**
C
CyC2018 已提交
2064 2065
 * place 位数的起始数字
 * 0, 10, 100, ...
C
CyC2018 已提交
2066
 */
C
CyC2018 已提交
2067
private int getBeginNumberOfPlace(int place) {
C
CyC2018 已提交
2068 2069 2070
    if (place == 1)
        return 0;
    return (int) Math.pow(10, place - 1);
C
CyC2018 已提交
2071 2072 2073
}

/**
C
CyC2018 已提交
2074
 * 在 place 位数组成的字符串中,第 index 个数
C
CyC2018 已提交
2075
 */
C
CyC2018 已提交
2076
private int getDigitAtIndex(int index, int place) {
C
CyC2018 已提交
2077 2078 2079 2080 2081
    int beginNumber = getBeginNumberOfPlace(place);
    int shiftNumber = index / place;
    String number = (beginNumber + shiftNumber) + "";
    int count = index % place;
    return number.charAt(count) - '0';
C
CyC2018 已提交
2082 2083 2084
}
```

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

C
CyC2018 已提交
2087 2088
[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 已提交
2089
## 题目描述
C
CyC2018 已提交
2090

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

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

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

```java
C
CyC2018 已提交
2098
public String PrintMinNumber(int[] numbers) {
C
CyC2018 已提交
2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109
    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 已提交
2110 2111 2112
}
```

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

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

C
CyC2018 已提交
2117
## 题目描述
C
CyC2018 已提交
2118

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

C
CyC2018 已提交
2121
## 解题思路
C
CyC2018 已提交
2122 2123

```java
C
CyC2018 已提交
2124
public int numDecodings(String s) {
C
CyC2018 已提交
2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141
    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 已提交
2142 2143 2144
}
```

C
CyC2018 已提交
2145
# 47. 礼物的最大价值
C
CyC2018 已提交
2146

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

C
CyC2018 已提交
2149
## 题目描述
C
CyC2018 已提交
2150

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

```
C
CyC2018 已提交
2154 2155 2156 2157
1    10   3    8
12   2    9    6
5    7    4    11
3    7    16   5
C
CyC2018 已提交
2158 2159
```

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

C
CyC2018 已提交
2162
## 解题思路
C
CyC2018 已提交
2163 2164 2165 2166

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

```java
C
CyC2018 已提交
2167
public int getMost(int[][] values) {
C
CyC2018 已提交
2168 2169
    if (values == null || values.length == 0 || values[0].length == 0)
        return 0;
C
CyC2018 已提交
2170
    int n = values[0].length;
C
CyC2018 已提交
2171
    int[] dp = new int[n];
C
CyC2018 已提交
2172 2173 2174 2175
    for (int[] value : values) {
        dp[0] += value[0];
        for (int i = 1; i < n; i++)
            dp[i] = Math.max(dp[i], dp[i - 1]) + value[i];
C
CyC2018 已提交
2176 2177
    }
    return dp[n - 1];
C
CyC2018 已提交
2178 2179 2180
}
```

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

C
CyC2018 已提交
2183
## 题目描述
C
CyC2018 已提交
2184

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

C
CyC2018 已提交
2187
## 解题思路
C
CyC2018 已提交
2188 2189

```java
C
CyC2018 已提交
2190
public int longestSubStringWithoutDuplication(String str) {
C
CyC2018 已提交
2191 2192 2193 2194
    int curLen = 0;
    int maxLen = 0;
    int[] preIndexs = new int[26];
    Arrays.fill(preIndexs, -1);
C
CyC2018 已提交
2195 2196 2197 2198
    for (int curI = 0; curI < str.length(); curI++) {
        int c = str.charAt(curI) - 'a';
        int preI = preIndexs[c];
        if (preI == -1 || curI - preI > curLen) {
C
CyC2018 已提交
2199 2200 2201
            curLen++;
        } else {
            maxLen = Math.max(maxLen, curLen);
C
CyC2018 已提交
2202
            curLen = curI - preI;
C
CyC2018 已提交
2203
        }
C
CyC2018 已提交
2204
        preIndexs[c] = curI;
C
CyC2018 已提交
2205 2206 2207
    }
    maxLen = Math.max(maxLen, curLen);
    return maxLen;
C
CyC2018 已提交
2208 2209 2210
}
```

C
CyC2018 已提交
2211
# 49. 丑数
C
CyC2018 已提交
2212

C
CyC2018 已提交
2213 2214
[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 已提交
2215
## 题目描述
C
CyC2018 已提交
2216

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

C
CyC2018 已提交
2219
## 解题思路
C
CyC2018 已提交
2220 2221

```java
C
CyC2018 已提交
2222
public int GetUglyNumber_Solution(int N) {
C
CyC2018 已提交
2223 2224
    if (N <= 6)
        return N;
C
CyC2018 已提交
2225
    int i2 = 0, i3 = 0, i5 = 0;
C
CyC2018 已提交
2226
    int[] dp = new int[N];
C
CyC2018 已提交
2227
    dp[0] = 1;
C
CyC2018 已提交
2228 2229 2230 2231
    for (int i = 1; i < N; i++) {
        int next2 = dp[i2] * 2, next3 = dp[i3] * 3, next5 = dp[i5] * 5;
        dp[i] = Math.min(next2, Math.min(next3, next5));
        if (dp[i] == next2)
C
CyC2018 已提交
2232
            i2++;
C
CyC2018 已提交
2233
        if (dp[i] == next3)
C
CyC2018 已提交
2234
            i3++;
C
CyC2018 已提交
2235
        if (dp[i] == next5)
C
CyC2018 已提交
2236 2237
            i5++;
    }
C
CyC2018 已提交
2238
    return dp[N - 1];
C
CyC2018 已提交
2239 2240 2241
}
```

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

C
CyC2018 已提交
2244 2245
[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 已提交
2246
## 题目描述
C
CyC2018 已提交
2247

C
CyC2018 已提交
2248
在一个字符串中找到第一个只出现一次的字符,并返回它的位置。
C
CyC2018 已提交
2249

C
CyC2018 已提交
2250
## 解题思路
C
CyC2018 已提交
2251

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

```java
C
CyC2018 已提交
2255
public int FirstNotRepeatingChar(String str) {
C
CyC2018 已提交
2256 2257 2258 2259 2260 2261 2262
    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 已提交
2263 2264 2265
}
```

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

```java
C
CyC2018 已提交
2269
public int FirstNotRepeatingChar2(String str) {
C
CyC2018 已提交
2270 2271 2272 2273
    BitSet bs1 = new BitSet(256);
    BitSet bs2 = new BitSet(256);
    for (char c : str.toCharArray()) {
        if (!bs1.get(c) && !bs2.get(c))
C
CyC2018 已提交
2274
            bs1.set(c);     // 0 0 -> 0 1
C
CyC2018 已提交
2275
        else if (bs1.get(c) && !bs2.get(c))
C
CyC2018 已提交
2276
            bs2.set(c);     // 0 1 -> 1 1
C
CyC2018 已提交
2277 2278 2279
    }
    for (int i = 0; i < str.length(); i++) {
        char c = str.charAt(i);
C
CyC2018 已提交
2280
        if (bs1.get(c) && !bs2.get(c))  // 0 1
C
CyC2018 已提交
2281 2282 2283
            return i;
    }
    return -1;
C
CyC2018 已提交
2284 2285 2286
}
```

C
CyC2018 已提交
2287
# 51. 数组中的逆序对
C
CyC2018 已提交
2288

C
CyC2018 已提交
2289 2290
[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 已提交
2291
## 题目描述
C
CyC2018 已提交
2292

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

C
CyC2018 已提交
2295
## 解题思路
C
CyC2018 已提交
2296 2297

```java
C
CyC2018 已提交
2298
private long cnt = 0;
C
CyC2018 已提交
2299
private int[] tmp;  // 在这里声明辅助数组,而不是在 merge() 递归函数中声明
C
CyC2018 已提交
2300

C
CyC2018 已提交
2301
public int InversePairs(int[] nums) {
C
CyC2018 已提交
2302 2303 2304
    tmp = new int[nums.length];
    mergeSort(nums, 0, nums.length - 1);
    return (int) (cnt % 1000000007);
C
CyC2018 已提交
2305 2306
}

C
CyC2018 已提交
2307
private void mergeSort(int[] nums, int l, int h) {
C
CyC2018 已提交
2308 2309 2310 2311 2312 2313
    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 已提交
2314 2315
}

C
CyC2018 已提交
2316
private void merge(int[] nums, int l, int m, int h) {
C
CyC2018 已提交
2317 2318 2319 2320 2321 2322 2323 2324 2325 2326
    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++];
C
CyC2018 已提交
2327
            this.cnt += m - i + 1;  // nums[i] >= nums[j],说明 nums[i...mid] 都大于 nums[j]
C
CyC2018 已提交
2328 2329 2330 2331 2332
        }
        k++;
    }
    for (k = l; k <= h; k++)
        nums[k] = tmp[k];
C
CyC2018 已提交
2333 2334 2335
}
```

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

C
CyC2018 已提交
2338 2339
[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 已提交
2340
## 题目描述
C
CyC2018 已提交
2341

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

C
CyC2018 已提交
2344
## 解题思路
C
CyC2018 已提交
2345

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

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

```java
C
CyC2018 已提交
2351
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
C
CyC2018 已提交
2352 2353 2354 2355 2356 2357
    ListNode l1 = pHead1, l2 = pHead2;
    while (l1 != l2) {
        l1 = (l1 == null) ? pHead2 : l1.next;
        l2 = (l2 == null) ? pHead1 : l2.next;
    }
    return l1;
C
CyC2018 已提交
2358 2359 2360
}
```

C
CyC2018 已提交
2361
# 53. 数字在排序数组中出现的次数
C
CyC2018 已提交
2362

C
CyC2018 已提交
2363 2364
[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 已提交
2365
## 题目描述
C
CyC2018 已提交
2366 2367 2368

```html
Input:
C
CyC2018 已提交
2369 2370 2371
nums = 1, 2, 3, 3, 3, 3, 4, 6
K = 3

C
CyC2018 已提交
2372 2373 2374 2375
Output:
4
```

C
CyC2018 已提交
2376
## 解题思路
C
CyC2018 已提交
2377 2378

```java
C
CyC2018 已提交
2379
public int GetNumberOfK(int[] nums, int K) {
C
CyC2018 已提交
2380 2381 2382
    int first = binarySearch(nums, K);
    int last = binarySearch(nums, K + 1);
    return (first == nums.length || nums[first] != K) ? 0 : last - first;
C
CyC2018 已提交
2383 2384
}

C
CyC2018 已提交
2385
private int binarySearch(int[] nums, int K) {
C
CyC2018 已提交
2386 2387 2388 2389 2390 2391 2392 2393 2394
    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 已提交
2395 2396 2397
}
```

C
CyC2018 已提交
2398
# 54. 二叉查找树的第 K 个结点
C
CyC2018 已提交
2399

C
CyC2018 已提交
2400 2401
[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 已提交
2402
## 解题思路
C
CyC2018 已提交
2403

C
CyC2018 已提交
2404
利用二叉查找树中序遍历有序的特点。
C
CyC2018 已提交
2405 2406

```java
C
CyC2018 已提交
2407 2408
private TreeNode ret;
private int cnt = 0;
C
CyC2018 已提交
2409

C
CyC2018 已提交
2410
public TreeNode KthNode(TreeNode pRoot, int k) {
C
CyC2018 已提交
2411 2412
    inOrder(pRoot, k);
    return ret;
C
CyC2018 已提交
2413 2414
}

C
CyC2018 已提交
2415
private void inOrder(TreeNode root, int k) {
C
CyC2018 已提交
2416 2417 2418 2419 2420 2421 2422
    if (root == null || cnt >= k)
        return;
    inOrder(root.left, k);
    cnt++;
    if (cnt == k)
        ret = root;
    inOrder(root.right, k);
C
CyC2018 已提交
2423 2424 2425
}
```

C
CyC2018 已提交
2426
# 55.1 二叉树的深度
C
CyC2018 已提交
2427

C
CyC2018 已提交
2428 2429
[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 已提交
2430
## 题目描述
C
CyC2018 已提交
2431 2432 2433

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

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

C
CyC2018 已提交
2436
## 解题思路
C
CyC2018 已提交
2437 2438

```java
C
CyC2018 已提交
2439
public int TreeDepth(TreeNode root) {
C
CyC2018 已提交
2440
    return root == null ? 0 : 1 + Math.max(TreeDepth(root.left), TreeDepth(root.right));
C
CyC2018 已提交
2441 2442 2443
}
```

C
CyC2018 已提交
2444
# 55.2 平衡二叉树
C
CyC2018 已提交
2445

C
CyC2018 已提交
2446 2447
[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 已提交
2448
## 题目描述
C
CyC2018 已提交
2449

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

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

C
CyC2018 已提交
2454
## 解题思路
C
CyC2018 已提交
2455 2456

```java
C
CyC2018 已提交
2457
private boolean isBalanced = true;
C
CyC2018 已提交
2458

C
CyC2018 已提交
2459
public boolean IsBalanced_Solution(TreeNode root) {
C
CyC2018 已提交
2460 2461
    height(root);
    return isBalanced;
C
CyC2018 已提交
2462 2463
}

C
CyC2018 已提交
2464
private int height(TreeNode root) {
C
CyC2018 已提交
2465
    if (root == null || !isBalanced)
C
CyC2018 已提交
2466 2467 2468 2469 2470 2471
        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 已提交
2472 2473 2474
}
```

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

C
CyC2018 已提交
2477 2478
[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 已提交
2479
## 题目描述
C
CyC2018 已提交
2480

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

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

C
CyC2018 已提交
2485
两个不相等的元素在位级表示上必定会有一位存在不同,将数组的所有元素异或得到的结果为不存在重复的两个元素异或的结果。
C
CyC2018 已提交
2486

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

```java
C
CyC2018 已提交
2490
public void FindNumsAppearOnce(int[] nums, int num1[], int num2[]) {
C
CyC2018 已提交
2491 2492 2493 2494 2495 2496 2497 2498 2499
    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 已提交
2500
    }
C
CyC2018 已提交
2501
}
C
CyC2018 已提交
2502 2503
```

C
CyC2018 已提交
2504
# 57.1 和为 S 的两个数字
C
CyC2018 已提交
2505

C
CyC2018 已提交
2506 2507
[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 已提交
2508
## 题目描述
C
CyC2018 已提交
2509

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

C
CyC2018 已提交
2512
## 解题思路
C
CyC2018 已提交
2513 2514 2515

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

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

```java
C
CyC2018 已提交
2521
public ArrayList<Integer> FindNumbersWithSum(int[] array, int sum) {
C
CyC2018 已提交
2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532
    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 已提交
2533 2534 2535
}
```

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

C
CyC2018 已提交
2538 2539
[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 已提交
2540
## 题目描述
C
CyC2018 已提交
2541

C
CyC2018 已提交
2542
输出所有和为 S 的连续正数序列。
C
CyC2018 已提交
2543

C
CyC2018 已提交
2544
例如和为 100 的连续序列有:
C
CyC2018 已提交
2545 2546

```
C
CyC2018 已提交
2547 2548
[9, 10, 11, 12, 13, 14, 15, 16]
[18, 19, 20, 21, 22]。
C
CyC2018 已提交
2549
```
C
CyC2018 已提交
2550

C
CyC2018 已提交
2551
## 解题思路
C
CyC2018 已提交
2552 2553

```java
C
CyC2018 已提交
2554
public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
C
CyC2018 已提交
2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576
    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 已提交
2577 2578 2579
}
```

C
CyC2018 已提交
2580
# 58.1 翻转单词顺序列
C
CyC2018 已提交
2581

C
CyC2018 已提交
2582 2583
[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 已提交
2584
## 题目描述
C
CyC2018 已提交
2585

C
CyC2018 已提交
2586 2587 2588
```html
Input:
"I am a student."
C
CyC2018 已提交
2589

C
CyC2018 已提交
2590 2591 2592
Output:
"student. a am I"
```
C
CyC2018 已提交
2593

C
CyC2018 已提交
2594
## 解题思路
C
CyC2018 已提交
2595

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

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

```java
C
CyC2018 已提交
2601
public String ReverseSentence(String str) {
C
CyC2018 已提交
2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613
    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 已提交
2614 2615
}

C
CyC2018 已提交
2616
private void reverse(char[] c, int i, int j) {
C
CyC2018 已提交
2617 2618
    while (i < j)
        swap(c, i++, j--);
C
CyC2018 已提交
2619
}
C
CyC2018 已提交
2620

C
CyC2018 已提交
2621
private void swap(char[] c, int i, int j) {
C
CyC2018 已提交
2622 2623 2624
    char t = c[i];
    c[i] = c[j];
    c[j] = t;
C
CyC2018 已提交
2625
}
C
CyC2018 已提交
2626 2627
```

C
CyC2018 已提交
2628
# 58.2 左旋转字符串
C
CyC2018 已提交
2629

C
CyC2018 已提交
2630 2631
[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 已提交
2632
## 题目描述
C
CyC2018 已提交
2633

C
CyC2018 已提交
2634 2635 2636 2637 2638 2639 2640 2641
```html
Input:
S="abcXYZdef"
K=3

Output:
"XYZdefabc"
```
C
CyC2018 已提交
2642

C
CyC2018 已提交
2643
## 解题思路
C
CyC2018 已提交
2644

C
CyC2018 已提交
2645
先将 "abc" 和 "XYZdef" 分别翻转,得到 "cbafedZYX",然后再把整个字符串翻转得到 "XYZdefabc"。
C
CyC2018 已提交
2646

C
CyC2018 已提交
2647
```java
C
CyC2018 已提交
2648
public String LeftRotateString(String str, int n) {
C
CyC2018 已提交
2649 2650 2651 2652 2653 2654 2655
    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 已提交
2656 2657
}

C
CyC2018 已提交
2658
private void reverse(char[] chars, int i, int j) {
C
CyC2018 已提交
2659 2660
    while (i < j)
        swap(chars, i++, j--);
C
CyC2018 已提交
2661
}
C
CyC2018 已提交
2662

C
CyC2018 已提交
2663
private void swap(char[] chars, int i, int j) {
C
CyC2018 已提交
2664 2665 2666
    char t = chars[i];
    chars[i] = chars[j];
    chars[j] = t;
C
CyC2018 已提交
2667
}
C
CyC2018 已提交
2668 2669
```

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

C
CyC2018 已提交
2672 2673
[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 已提交
2674
## 题目描述
C
CyC2018 已提交
2675

C
CyC2018 已提交
2676 2677 2678
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。

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

C
CyC2018 已提交
2680
## 解题思路
C
CyC2018 已提交
2681 2682

```java
C
CyC2018 已提交
2683
public ArrayList<Integer> maxInWindows(int[] num, int size) {
C
CyC2018 已提交
2684 2685 2686
    ArrayList<Integer> ret = new ArrayList<>();
    if (size > num.length || size < 1)
        return ret;
C
CyC2018 已提交
2687
    PriorityQueue<Integer> heap = new PriorityQueue<>((o1, o2) -> o2 - o1);  /* 大顶堆 */
C
CyC2018 已提交
2688 2689 2690
    for (int i = 0; i < size; i++)
        heap.add(num[i]);
    ret.add(heap.peek());
C
CyC2018 已提交
2691
    for (int i = 1, j = i + size - 1; j < num.length; i++, j++) {            /* 维护一个大小为 size 的大顶堆 */
C
CyC2018 已提交
2692 2693 2694 2695 2696
        heap.remove(num[i - 1]);
        heap.add(num[j]);
        ret.add(heap.peek());
    }
    return ret;
C
CyC2018 已提交
2697 2698 2699
}
```

C
CyC2018 已提交
2700
# 60. n 个骰子的点数
C
CyC2018 已提交
2701

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

C
CyC2018 已提交
2704
## 题目描述
C
CyC2018 已提交
2705

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

C
CyC2018 已提交
2708
## 解题思路
C
CyC2018 已提交
2709

C
CyC2018 已提交
2710
### 动态规划解法
C
CyC2018 已提交
2711

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

C
CyC2018 已提交
2714 2715 2716
空间复杂度:O(N<sup>2</sup>)

```java
C
CyC2018 已提交
2717
public List<Map.Entry<Integer, Double>> dicesSum(int n) {
C
CyC2018 已提交
2718 2719 2720
    final int face = 6;
    final int pointNum = face * n;
    long[][] dp = new long[n + 1][pointNum + 1];
C
CyC2018 已提交
2721

C
CyC2018 已提交
2722 2723
    for (int i = 1; i <= face; i++)
        dp[1][i] = 1;
C
CyC2018 已提交
2724

C
CyC2018 已提交
2725
    for (int i = 2; i <= n; i++)
C
CyC2018 已提交
2726
        for (int j = i; j <= pointNum; j++)     /* 使用 i 个骰子最小点数为 i */
C
CyC2018 已提交
2727 2728
            for (int k = 1; k <= face && k <= j; k++)
                dp[i][j] += dp[i - 1][j - k];
C
CyC2018 已提交
2729

C
CyC2018 已提交
2730 2731 2732 2733
    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));
C
CyC2018 已提交
2734

C
CyC2018 已提交
2735
    return ret;
C
CyC2018 已提交
2736 2737 2738
}
```

C
CyC2018 已提交
2739
### 动态规划解法 + 旋转数组
C
CyC2018 已提交
2740 2741 2742 2743

空间复杂度:O(N)

```java
C
CyC2018 已提交
2744
public List<Map.Entry<Integer, Double>> dicesSum(int n) {
C
CyC2018 已提交
2745 2746 2747
    final int face = 6;
    final int pointNum = face * n;
    long[][] dp = new long[2][pointNum + 1];
C
CyC2018 已提交
2748

C
CyC2018 已提交
2749 2750
    for (int i = 1; i <= face; i++)
        dp[0][i] = 1;
C
CyC2018 已提交
2751 2752

    int flag = 1;                                     /* 旋转标记 */
C
CyC2018 已提交
2753 2754
    for (int i = 2; i <= n; i++, flag = 1 - flag) {
        for (int j = 0; j <= pointNum; j++)
C
CyC2018 已提交
2755 2756 2757
            dp[flag][j] = 0;                          /* 旋转数组清零 */

        for (int j = i; j <= pointNum; j++)
C
CyC2018 已提交
2758 2759 2760
            for (int k = 1; k <= face && k <= j; k++)
                dp[flag][j] += dp[1 - flag][j - k];
    }
C
CyC2018 已提交
2761

C
CyC2018 已提交
2762 2763 2764 2765
    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));
C
CyC2018 已提交
2766

C
CyC2018 已提交
2767
    return ret;
C
CyC2018 已提交
2768 2769 2770
}
```

C
CyC2018 已提交
2771
# 61. 扑克牌顺子
C
CyC2018 已提交
2772

C
CyC2018 已提交
2773 2774
[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 已提交
2775
## 题目描述
C
CyC2018 已提交
2776

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

C
CyC2018 已提交
2779
## 解题思路
C
CyC2018 已提交
2780 2781

```java
C
CyC2018 已提交
2782
public boolean isContinuous(int[] nums) {
C
CyC2018 已提交
2783

C
CyC2018 已提交
2784 2785
    if (nums.length < 5)
        return false;
C
CyC2018 已提交
2786

C
CyC2018 已提交
2787
    Arrays.sort(nums);
C
CyC2018 已提交
2788 2789

    // 统计癞子数量
C
CyC2018 已提交
2790
    int cnt = 0;
C
CyC2018 已提交
2791
    for (int num : nums)
C
CyC2018 已提交
2792 2793
        if (num == 0)
            cnt++;
C
CyC2018 已提交
2794

C
CyC2018 已提交
2795
    // 使用癞子去补全不连续的顺子
C
CyC2018 已提交
2796 2797 2798
    for (int i = cnt; i < nums.length - 1; i++) {
        if (nums[i + 1] == nums[i])
            return false;
C
CyC2018 已提交
2799
        cnt -= nums[i + 1] - nums[i] - 1;
C
CyC2018 已提交
2800
    }
C
CyC2018 已提交
2801

C
CyC2018 已提交
2802
    return cnt >= 0;
C
CyC2018 已提交
2803 2804 2805
}
```

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

C
CyC2018 已提交
2808 2809
[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 已提交
2810
## 题目描述
C
CyC2018 已提交
2811

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

C
CyC2018 已提交
2814
## 解题思路
C
CyC2018 已提交
2815

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

```java
C
CyC2018 已提交
2819
public int LastRemaining_Solution(int n, int m) {
C
CyC2018 已提交
2820
    if (n == 0)     /* 特殊输入的处理 */
C
CyC2018 已提交
2821
        return -1;
C
CyC2018 已提交
2822
    if (n == 1)     /* 递归返回条件 */
C
CyC2018 已提交
2823 2824
        return 0;
    return (LastRemaining_Solution(n - 1, m) + m) % n;
C
CyC2018 已提交
2825 2826 2827
}
```

C
CyC2018 已提交
2828
# 63. 股票的最大利润
C
CyC2018 已提交
2829

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

C
CyC2018 已提交
2832
## 题目描述
C
CyC2018 已提交
2833

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

C
CyC2018 已提交
2836
## 解题思路
C
CyC2018 已提交
2837

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

```java
C
CyC2018 已提交
2841
public int maxProfit(int[] prices) {
C
CyC2018 已提交
2842 2843 2844 2845
    if (prices == null || prices.length == 0)
        return 0;
    int soFarMin = prices[0];
    int maxProfit = 0;
C
CyC2018 已提交
2846
    for (int i = 1; i < prices.length; i++) {
C
CyC2018 已提交
2847 2848 2849 2850
        soFarMin = Math.min(soFarMin, prices[i]);
        maxProfit = Math.max(maxProfit, prices[i] - soFarMin);
    }
    return maxProfit;
C
CyC2018 已提交
2851 2852 2853
}
```

C
CyC2018 已提交
2854
# 64. 求 1+2+3+...+n
C
CyC2018 已提交
2855

C
CyC2018 已提交
2856 2857
[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 已提交
2858
## 题目描述
C
CyC2018 已提交
2859

C
CyC2018 已提交
2860
要求不能使用乘除法、for、while、if、else、switch、case 等关键字及条件判断语句 A ? B : C。
C
CyC2018 已提交
2861

C
CyC2018 已提交
2862
## 解题思路
C
CyC2018 已提交
2863

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

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

C
CyC2018 已提交
2868
本题的递归返回条件为 n <= 0,取非后就是 n > 0;递归的主体部分为 sum += Sum_Solution(n - 1),转换为条件语句后就是 (sum += Sum_Solution(n - 1)) > 0。
C
CyC2018 已提交
2869

C
CyC2018 已提交
2870
```java
C
CyC2018 已提交
2871
public int Sum_Solution(int n) {
C
CyC2018 已提交
2872 2873 2874
    int sum = n;
    boolean b = (n > 0) && ((sum += Sum_Solution(n - 1)) > 0);
    return sum;
C
CyC2018 已提交
2875 2876 2877
}
```

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

C
CyC2018 已提交
2880 2881
[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 已提交
2882
## 题目描述
C
CyC2018 已提交
2883

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

C
CyC2018 已提交
2886
## 解题思路
C
CyC2018 已提交
2887

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

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

```java
C
CyC2018 已提交
2893
public int Add(int a, int b) {
C
CyC2018 已提交
2894
    return b == 0 ? a : Add(a ^ b, (a & b) << 1);
C
CyC2018 已提交
2895 2896 2897
}
```

C
CyC2018 已提交
2898
# 66. 构建乘积数组
C
CyC2018 已提交
2899

C
CyC2018 已提交
2900 2901
[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 已提交
2902
## 题目描述
C
CyC2018 已提交
2903

C
CyC2018 已提交
2904
给定一个数组 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 已提交
2905

C
CyC2018 已提交
2906
## 解题思路
C
CyC2018 已提交
2907 2908

```java
C
CyC2018 已提交
2909
public int[] multiply(int[] A) {
C
CyC2018 已提交
2910 2911
    int n = A.length;
    int[] B = new int[n];
C
CyC2018 已提交
2912
    for (int i = 0, product = 1; i < n; product *= A[i], i++)       /* 从左往右累乘 */
C
CyC2018 已提交
2913
        B[i] = product;
C
CyC2018 已提交
2914
    for (int i = n - 1, product = 1; i >= 0; product *= A[i], i--)  /* 从右往左累乘 */
C
CyC2018 已提交
2915 2916
        B[i] *= product;
    return B;
C
CyC2018 已提交
2917 2918 2919
}
```

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

C
CyC2018 已提交
2922 2923
[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 已提交
2924
## 题目描述
C
CyC2018 已提交
2925

C
CyC2018 已提交
2926
将一个字符串转换成一个整数,字符串不是一个合法的数值则返回 0,要求不能使用字符串转换整数的库函数。
C
CyC2018 已提交
2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937

```html
Iuput:
+2147483647
1a33

Output:
2147483647
0
```

C
CyC2018 已提交
2938
## 解题思路
C
CyC2018 已提交
2939 2940

```java
C
CyC2018 已提交
2941
public int StrToInt(String str) {
C
CyC2018 已提交
2942 2943 2944 2945 2946 2947
    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);
C
CyC2018 已提交
2948
        if (i == 0 && (c == '+' || c == '-'))  /* 符号判定 */
C
CyC2018 已提交
2949
            continue;
C
CyC2018 已提交
2950 2951
        if (c < '0' || c > '9')                /* 非法输入 */
            return 0;
C
CyC2018 已提交
2952 2953 2954
        ret = ret * 10 + (c - '0');
    }
    return isNegative ? -ret : ret;
C
CyC2018 已提交
2955 2956 2957
}
```

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

C
CyC2018 已提交
2960
## 解题思路
C
CyC2018 已提交
2961

C
CyC2018 已提交
2962
### 二叉查找树
C
CyC2018 已提交
2963

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

C
CyC2018 已提交
2966
[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 已提交
2967

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

```java
C
CyC2018 已提交
2971 2972 2973 2974 2975 2976 2977 2978
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 已提交
2979 2980 2981
}
```

C
CyC2018 已提交
2982
### 普通二叉树
C
CyC2018 已提交
2983

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

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

C
CyC2018 已提交
2988
在左右子树中查找是否存在 p 或者 q,如果 p 和 q 分别在两个子树中,那么就说明根节点就是最低公共祖先。
C
CyC2018 已提交
2989 2990

```java
C
CyC2018 已提交
2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002
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.