剑指 offer 题解.md 93.5 KB
Newer Older
C
CyC2018 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
<!-- GFM-TOC -->
* [2. 实现 Singleton](#2-实现-singleton)
* [3. 数组中重复的数字](#3-数组中重复的数字)
* [4. 二维数组中的查找](#4-二维数组中的查找)
* [5. 替换空格](#5-替换空格)
* [6. 从尾到头打印链表](#6-从尾到头打印链表)
* [7. 重建二叉树](#7-重建二叉树)
* [8. 二叉树的下一个结点](#8-二叉树的下一个结点)
* [9. 用两个栈实现队列](#9-用两个栈实现队列)
* [10.1 斐波那契数列](#101-斐波那契数列)
* [10.2 跳台阶](#102-跳台阶)
* [10.3 变态跳台阶](#103-变态跳台阶)
* [10.4 矩形覆盖](#104-矩形覆盖)
* [11. 旋转数组的最小数字](#11-旋转数组的最小数字)
* [12. 矩阵中的路径](#12-矩阵中的路径)
* [13. 机器人的运动范围](#13-机器人的运动范围)
* [14. 剪绳子](#14-剪绳子)
* [15. 二进制中 1 的个数](#15-二进制中-1-的个数)
* [16. 数值的整数次方](#16-数值的整数次方)
* [17. 打印从 1 到最大的 n 位数](#17-打印从-1-到最大的-n-位数)
* [18.1 在 O(1) 时间内删除链表节点](#181-在-o1-时间内删除链表节点)
* [18.2 删除链表中重复的结点](#182-删除链表中重复的结点)
* [19. 正则表达式匹配](#19-正则表达式匹配)
* [20. 表示数值的字符串](#20-表示数值的字符串)
* [21. 调整数组顺序使奇数位于偶数前面](#21-调整数组顺序使奇数位于偶数前面)
* [22. 链表中倒数第 K 个结点](#22-链表中倒数第-k-个结点)
* [23. 链表中环的入口结点](#23-链表中环的入口结点)
* [24. 反转链表](#24-反转链表)
* [25. 合并两个排序的链表](#25-合并两个排序的链表)
* [26. 树的子结构](#26-树的子结构)
* [27. 二叉树的镜像](#27-二叉树的镜像)
* [28 对称的二叉树](#28-对称的二叉树)
* [29. 顺时针打印矩阵](#29-顺时针打印矩阵)
* [30. 包含 min 函数的栈](#30-包含-min-函数的栈)
* [31. 栈的压入、弹出序列](#31-栈的压入弹出序列)
* [32.1 从上往下打印二叉树](#321-从上往下打印二叉树)
* [32.2 把二叉树打印成多行](#322-把二叉树打印成多行)
* [32.3 按之字形顺序打印二叉树](#323-按之字形顺序打印二叉树)
* [33. 二叉搜索树的后序遍历序列](#33-二叉搜索树的后序遍历序列)
* [34. 二叉树中和为某一值的路径](#34-二叉树中和为某一值的路径)
* [35. 复杂链表的复制](#35-复杂链表的复制)
* [36. 二叉搜索树与双向链表](#36-二叉搜索树与双向链表)
* [37. 序列化二叉树](#37-序列化二叉树)
* [38. 字符串的排列](#38-字符串的排列)
* [39. 数组中出现次数超过一半的数字](#39-数组中出现次数超过一半的数字)
* [40. 最小的 K 个数](#40-最小的-k-个数)
* [41.1 数据流中的中位数](#411-数据流中的中位数)
* [41.2 字符流中第一个不重复的字符](#412-字符流中第一个不重复的字符)
* [42. 连续子数组的最大和](#42-连续子数组的最大和)
* [43. 从 1 到 n 整数中 1 出现的次数](#43-从-1-到-n-整数中-1-出现的次数)
* [44. 数字序列中的某一位数字](#44-数字序列中的某一位数字)
* [45. 把数组排成最小的数](#45-把数组排成最小的数)
* [46. 把数字翻译成字符串](#46-把数字翻译成字符串)
* [47. 礼物的最大价值](#47-礼物的最大价值)
* [48. 最长不含重复字符的子字符串](#48-最长不含重复字符的子字符串)
* [49. 丑数](#49-丑数)
* [50. 第一个只出现一次的字符位置](#50-第一个只出现一次的字符位置)
* [51. 数组中的逆序对](#51-数组中的逆序对)
* [52. 两个链表的第一个公共结点](#52-两个链表的第一个公共结点)
* [53 数字在排序数组中出现的次数](#53-数字在排序数组中出现的次数)
* [54. 二叉搜索树的第 K 个结点](#54-二叉搜索树的第-k-个结点)
* [55.1 二叉树的深度](#551-二叉树的深度)
* [55.2 平衡二叉树](#552-平衡二叉树)
* [56. 数组中只出现一次的数字](#56-数组中只出现一次的数字)
* [57.1 和为 S 的两个数字](#571-和为-s-的两个数字)
* [57.2 和为 S 的连续正数序列](#572-和为-s-的连续正数序列)
* [58.1 翻转单词顺序列](#581-翻转单词顺序列)
* [58.2 左旋转字符串](#582-左旋转字符串)
* [59. 滑动窗口的最大值](#59-滑动窗口的最大值)
* [60. n 个骰子的点数](#60-n-个骰子的点数)
* [61. 扑克牌顺子](#61-扑克牌顺子)
* [62. 圆圈中最后剩下的数](#62-圆圈中最后剩下的数)
* [63. 股票的最大利润](#63-股票的最大利润)
C
CyC2018 已提交
74
* [64. 求 1+2+3+...+n](#64-求-123n)
C
CyC2018 已提交
75 76 77 78 79 80 81 82 83 84
* [65. 不用加减乘除做加法](#65-不用加减乘除做加法)
* [66. 构建乘积数组](#66-构建乘积数组)
* [67. 把字符串转换成整数](#67-把字符串转换成整数)
* [68. 树中两个节点的最低公共祖先](#68-树中两个节点的最低公共祖先)
* [参考文献](#参考文献)
<!-- GFM-TOC -->


# 2. 实现 Singleton

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

# 3. 数组中重复的数字

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

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

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

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

## 解题思路

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

以 (2, 3, 1, 0, 2, 5) 为例:

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

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

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

private void swap(int[] nums, int i, int j) {
    int t = nums[i]; nums[i] = nums[j]; nums[j] = t;
}
```

# 4. 二维数组中的查找

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

C
CyC2018 已提交
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
## 题目描述

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

```html
Consider the following matrix:
[
  [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]
]

Given target = 5, return true.
Given target = 20, return false.
```

## 解题思路

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

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

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

# 5. 替换空格

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

C
CyC2018 已提交
187 188 189 190 191 192 193 194
## 题目描述

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

## 解题思路

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

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

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

复杂度:O(N) + O(1)

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

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

# 6. 从尾到头打印链表

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

C
CyC2018 已提交
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
## 题目描述

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

<div align="center"> <img src="../pics//d99dc9e2-197c-4085-813d-7195da1c6762.png" width="300"/> </div><br>

## 解题思路

### 使用栈

```java
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<>();
C
CyC2018 已提交
245
    while (!stack.isEmpty())
C
CyC2018 已提交
246 247 248 249 250 251 252 253 254 255
        ret.add(stack.pop());
    return ret;
}
```

### 使用递归

```java
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
    ArrayList<Integer> ret = new ArrayList<>();
C
CyC2018 已提交
256
    if (listNode != null) {
C
CyC2018 已提交
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
        ret.addAll(printListFromTailToHead(listNode.next));
        ret.add(listNode.val);
    }
    return ret;
}
```

### 使用 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;
}
```

### 使用头插法

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

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

```java
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;
}
```

# 7. 重建二叉树

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

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

C
CyC2018 已提交
311
根据二叉树的前序遍历和中序遍历的结果,重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
C
CyC2018 已提交
312 313 314 315 316 317 318 319 320 321 322 323 324

```html
preorder = [3,9,20,15,7]
inorder =  [9,3,15,20,7]
```

<div align="center"> <img src="../pics//8a4c6ad4-a816-47d1-b93f-7ca4f78ab67a.png" width="250"/> </div><br>

## 解题思路

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

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

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

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

# 8. 二叉树的下一个结点

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

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

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

## 解题思路

C
CyC2018 已提交
355
① 如果一个节点的右子树不为空,那么该节点的下一个节点是右子树的最左节点;
C
CyC2018 已提交
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379

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

```java
public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;

    TreeLinkNode(int val) {
        this.val = val;
    }
}
```

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

# 9. 用两个栈实现队列

C
CyC2018 已提交
397 398 399 400 401 402
[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)

## 题目描述

用两个栈来实现一个队列,完成队列的 Push 和 Pop 操作。队列中的元素为 int 类型。

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

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

<div align="center"> <img src="../pics//5acf7550-86c5-4c5b-b912-8ce70ef9c34e.png" width="400"/> </div><br>

```java
Stack<Integer> in = new Stack<Integer>();
Stack<Integer> out = new Stack<Integer>();

public void push(int node) {
    in.push(node);
}

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

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

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

# 10.1 斐波那契数列

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

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

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

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

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

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

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

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

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

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

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

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

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

# 10.2 跳台阶

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

C
CyC2018 已提交
499 500 501 502 503 504
## 题目描述

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

## 解题思路

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

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

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

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

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

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

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

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

## 解题思路

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

# 10.4 矩形覆盖

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

C
CyC2018 已提交
562 563 564 565 566 567
## 题目描述

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

## 解题思路

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

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

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

```java
public int RectCover(int n) {
C
CyC2018 已提交
587 588
    if (n <= 2)
        return n;
C
CyC2018 已提交
589 590 591 592 593 594 595 596 597 598 599
    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 已提交
600 601
# 11. 旋转数组的最小数字

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 605 606 607 608 609
## 题目描述

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

## 解题思路

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

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

复杂度:O(logN) + O(1)

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

# 12. 矩阵中的路径

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

C
CyC2018 已提交
636 637 638 639 640 641 642 643 644 645 646
## 题目描述

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

例如下面的矩阵包含了一条 bfce 路径。

<div align="center"> <img src="../pics//e31abb94-9201-4e06-9902-61101b92f475.png" width="300"/> </div><br>

## 解题思路

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

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

C
CyC2018 已提交
665 666 667 668 669 670 671 672
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]))
C
CyC2018 已提交
673
            return true;
C
CyC2018 已提交
674
    marked[r][c] = false;
C
CyC2018 已提交
675 676 677 678 679
    return false;
}

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

# 13. 机器人的运动范围

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

C
CyC2018 已提交
691 692 693 694 695 696 697
## 题目描述

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

## 解题思路

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

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

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

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 已提交
735
    digitSum = new int[rows][cols];
C
CyC2018 已提交
736 737
    for (int i = 0; i < this.rows; i++)
        for (int j = 0; j < this.cols; j++)
C
CyC2018 已提交
738
            digitSum[i][j] = digitSumOne[i] + digitSumOne[j];
C
CyC2018 已提交
739 740 741 742 743
}
```

# 14. 剪绳子

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

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

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

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

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

### 动态规划解法

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

### 贪心解法

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

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

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

# 15. 二进制中 1 的个数

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

C
CyC2018 已提交
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829
## 题目描述

输入一个整数,输出该数二进制表示中 1 的个数。

### Integer.bitCount()

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

### n&(n-1)

O(logM) 时间复杂度解法,其中 M 表示 1 的个数。

该位运算是去除 n 的位级表示中最低的那一位。

```
n       : 10110100
n-1     : 10110011
n&(n-1) : 10110000
```

```java
public int NumberOf1(int n) {
    int cnt = 0;
    while (n != 0) {
        cnt++;
        n &= (n - 1);
    }
    return cnt;
}
```

# 16. 数值的整数次方

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

C
CyC2018 已提交
832 833 834 835 836 837 838 839 840 841 842 843 844 845
## 题目描述

给定一个 double 类型的浮点数 base 和 int 类型的整数 exponent。求 base 的 exponent 次方。

## 解题思路

下面的讨论中 x 代表 base,n 代表 exponent。

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

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

```java
public double Power(double base, int exponent) {
C
CyC2018 已提交
846 847 848 849
    if (exponent == 0)
        return 1;
    if (exponent == 1)
        return base;
C
CyC2018 已提交
850
    boolean isNegative = false;
C
CyC2018 已提交
851
    if (exponent < 0) {
C
CyC2018 已提交
852 853 854
        exponent = -exponent;
        isNegative = true;
    }
C
CyC2018 已提交
855 856 857
    double pow = Power(base * base, exponent / 2);
    if (exponent % 2 != 0)
        pow = pow * base;
C
CyC2018 已提交
858
    return isNegative ? 1 / pow : pow;
C
CyC2018 已提交
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
}
```

# 17. 打印从 1 到最大的 n 位数

## 题目描述

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

## 解题思路

由于 n 可能会非常大,因此不能直接用 int 表示数字,而是用 char 数组进行存储。

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

```java
public void print1ToMaxOfNDigits(int n) {
C
CyC2018 已提交
876 877
    if (n <= 0)
        return;
C
CyC2018 已提交
878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894
    char[] number = new char[n];
    print1ToMaxOfNDigits(number, -1);
}

private void print1ToMaxOfNDigits(char[] number, int digit) {
    if (digit == number.length - 1) {
        printNumber(number);
        return;
    }
    for (int i = 0; i < 10; i++) {
        number[digit + 1] = (char) (i + '0');
        print1ToMaxOfNDigits(number, digit + 1);
    }
}

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

# 18.1 在 O(1) 时间内删除链表节点

## 解题思路

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

<div align="center"> <img src="../pics//41392d76-dd1d-4712-85d9-e8bb46b04a2d.png" width="600"/> </div><br>

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

<div align="center"> <img src="../pics//db4921d4-184b-48ba-a3cf-1d1141e3ba2d.png" width="600"/> </div><br>

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

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

# 18.2 删除链表中重复的结点

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

C
CyC2018 已提交
940 941 942 943 944 945 946 947
## 题目描述

<div align="center"> <img src="../pics//8433fbb2-c35c-45ef-831d-e3ca42aebd51.png" width="500"/> </div><br>

## 解题描述

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

# 19. 正则表达式匹配

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

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

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

## 解题思路

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

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

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

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

# 20. 表示数值的字符串

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

C
CyC2018 已提交
1012 1013 1014 1015 1016 1017 1018 1019
## 题目描述

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

## 解题思路

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

# 21. 调整数组顺序使奇数位于偶数前面

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

C
CyC2018 已提交
1030 1031 1032 1033 1034 1035 1036 1037
## 题目描述

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

## 解题思路

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

# 22. 链表中倒数第 K 个结点

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

C
CyC2018 已提交
1058 1059 1060 1061 1062 1063 1064 1065
## 解题思路

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

<div align="center"> <img src="../pics//207c1801-2335-4b1b-b65c-126a0ba966cb.png" width="500"/> </div><br>

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

# 23. 链表中环的入口结点

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

C
CyC2018 已提交
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
## 解题思路

使用双指针,一个指针 fast 每次移动两个节点,一个指针 slow 每次移动一个节点。因为存在环,所以两个指针必定相遇在环中的某个节点上。此时 fast 移动的节点数为 x+2y+z,slow 为 x+y,由于 fast 速度比 slow 快一倍,因此 x+2y+z=2(x+y),得到 x=z。

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

<div align="center"> <img src="../pics//71363383-2d06-4c63-8b72-c01c2186707d.png" width="600"/> </div><br>

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

# 24. 反转链表

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

### 递归

```java
public ListNode ReverseList(ListNode head) {
C
CyC2018 已提交
1125 1126
    if (head == null || head.next == null)
        return head;
C
CyC2018 已提交
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
    ListNode next = head.next;
    head.next = null;
    ListNode newHead = ReverseList(next);
    next.next = head;
    return newHead;
}
```

### 迭代

```java
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;
}
```

# 25. 合并两个排序的链表

C
CyC2018 已提交
1152 1153
[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 已提交
1154 1155 1156 1157 1158 1159 1160 1161 1162 1163
## 题目描述

<div align="center"> <img src="../pics//43f2cafa-3568-4a89-a895-4725666b94a6.png" width="500"/> </div><br>

## 解题思路

### 递归

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

### 迭代

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

# 26. 树的子结构

C
CyC2018 已提交
1204 1205
[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 已提交
1206 1207 1208 1209 1210 1211 1212 1213
## 题目描述

<div align="center"> <img src="../pics//4583e24f-424b-4d50-8a14-2c38a1827d4a.png" width="500"/> </div><br>

## 解题思路

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

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

# 27. 二叉树的镜像

C
CyC2018 已提交
1232 1233
[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 已提交
1234 1235 1236 1237 1238 1239 1240 1241
## 题目描述

<div align="center"> <img src="../pics//a2d13178-f1ef-4811-a240-1fe95b55b1eb.png" width="300"/> </div><br>

## 解题思路

```java
public void Mirror(TreeNode root) {
C
CyC2018 已提交
1242 1243
    if (root == null)
        return;
C
CyC2018 已提交
1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257
    swap(root);
    Mirror(root.left);
    Mirror(root.right);
}

private void swap(TreeNode root) {
    TreeNode t = root.left;
    root.left = root.right;
    root.right = t;
}
```

# 28 对称的二叉树

C
CyC2018 已提交
1258 1259
[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 已提交
1260 1261 1262 1263 1264 1265 1266 1267
## 题目描述

<div align="center"> <img src="../pics//f42443e0-208d-41ea-be44-c7fd97d2e3bf.png" width="300"/> </div><br>

## 解题思路

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

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

# 29. 顺时针打印矩阵

C
CyC2018 已提交
1286 1287
[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 已提交
1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300
## 题目描述

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

<div align="center"> <img src="../pics//0f373947-c68f-45b4-a59e-086154745ac5.png" width="300"/> </div><br>

## 解题思路

```java
public ArrayList<Integer> printMatrix(int[][] matrix) {
    ArrayList<Integer> ret = new ArrayList<>();
    int r1 = 0, r2 = matrix.length - 1, c1 = 0, c2 = matrix[0].length - 1;
    while (r1 <= r2 && c1 <= c2) {
C
CyC2018 已提交
1301 1302 1303 1304 1305 1306 1307 1308 1309 1310
        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]);
C
CyC2018 已提交
1311 1312 1313 1314 1315 1316 1317 1318
        r1++; r2--; c1++; c2--;
    }
    return ret;
}
```

# 30. 包含 min 函数的栈

C
CyC2018 已提交
1319 1320
[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 已提交
1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332
## 题目描述

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

## 解题思路

```java
private Stack<Integer> stack = new Stack<>();
private Stack<Integer> minStack = new Stack<>();

public void push(int node) {
    stack.push(node);
C
CyC2018 已提交
1333
    minStack.push(minStack.isEmpty() ? node : Math.min(minStack.peek(), node));
C
CyC2018 已提交
1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351
}

public void pop() {
    stack.pop();
    minStack.pop();
}

public int top() {
    return stack.peek();
}

public int min() {
    return minStack.peek();
}
```

# 31. 栈的压入、弹出序列

C
CyC2018 已提交
1352 1353
[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 已提交
1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378
## 题目描述

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

## 解题思路

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

```java
public boolean IsPopOrder(int[] pushA, int[] popA) {
    int n = pushA.length;
    Stack<Integer> stack = new Stack<>();
    for (int pushIndex = 0, popIndex = 0; pushIndex < n; pushIndex++) {
        stack.push(pushA[pushIndex]);
        while (popIndex < n && stack.peek() == popA[popIndex]) {
            stack.pop();
            popIndex++;
        }
    }
    return stack.isEmpty();
}
```

# 32.1 从上往下打印二叉树

C
CyC2018 已提交
1379 1380
[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 已提交
1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398
## 题目描述

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

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

<div align="center"> <img src="../pics//348bc2db-582e-4aca-9f88-38c40e9a0e69.png" width="250"/> </div><br>

## 解题思路

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

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

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

# 32.2 把二叉树打印成多行

C
CyC2018 已提交
1419 1420
[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 已提交
1421 1422 1423 1424 1425 1426 1427 1428 1429
## 题目描述

和上题几乎一样。

## 解题思路

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

# 32.3 按之字形顺序打印二叉树

C
CyC2018 已提交
1453 1454
[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 已提交
1455 1456 1457 1458 1459 1460 1461 1462 1463
## 题目描述

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

## 解题思路

```java
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
    ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
C
CyC2018 已提交
1464 1465
    if (pRoot == null)
        return ret;
C
CyC2018 已提交
1466 1467 1468 1469 1470
    Queue<TreeNode> queue = new LinkedList<>();
    queue.add(pRoot);
    boolean reverse = false;
    while (!queue.isEmpty()) {
        ArrayList<Integer> list = new ArrayList<>();
C
CyC2018 已提交
1471 1472
        int cnt = queue.size();
        while (cnt-- > 0) {
C
CyC2018 已提交
1473 1474
            TreeNode node = queue.poll();
            list.add(node.val);
C
CyC2018 已提交
1475 1476 1477 1478
            if (node.left != null)
                queue.add(node.left);
            if (node.right != null)
                queue.add(node.right);
C
CyC2018 已提交
1479
        }
C
CyC2018 已提交
1480 1481
        if (reverse)
            Collections.reverse(list);
C
CyC2018 已提交
1482 1483 1484 1485 1486 1487 1488 1489 1490
        reverse = !reverse;
        ret.add(list);
    }
    return ret;
}
```

# 33. 二叉搜索树的后序遍历序列

C
CyC2018 已提交
1491 1492
[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 已提交
1493 1494
## 题目描述

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

例如,下图是后序遍历序列 3,1,2 所对应的二叉搜索树。

<div align="center"> <img src="../pics//836a4eaf-4798-4e48-b52a-a3dab9435ace.png" width="150"/> </div><br>

## 解题思路

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

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

# 34. 二叉树中和为某一值的路径

C
CyC2018 已提交
1526 1527
[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 已提交
1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541
## 题目描述

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

下图的二叉树有两条和为 22 的路径:10, 5, 7 和 10, 12

<div align="center"> <img src="../pics//f5477abd-c246-4851-89ab-6b1cde2549b1.png" width="200"/> </div><br>

## 解题思路

```java
private ArrayList<ArrayList<Integer>> ret = new ArrayList<>();

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

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

# 35. 复杂链表的复制

C
CyC2018 已提交
1563 1564
[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 已提交
1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586
## 题目描述

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

<div align="center"> <img src="../pics//a01d1516-8168-461a-a24b-620b9cfc40f4.png" width="300"/> </div><br>

## 解题思路

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

<div align="center"> <img src="../pics//2e6c72f5-3b8e-4e32-b87b-9491322628fe.png" width="600"/> </div><br>

第二步,对复制节点的 random 链接进行赋值。

<div align="center"> <img src="../pics//323ffd6c-8b54-4f3e-b361-555a6c8bf218.png" width="600"/> </div><br>

第三步,拆分。

<div align="center"> <img src="../pics//8f3b9519-d705-48fe-87ad-2e4052fc81d2.png" width="600"/> </div><br>

```java
public RandomListNode Clone(RandomListNode pHead) {
C
CyC2018 已提交
1587
    if (pHead == null)
C
CyC2018 已提交
1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600
        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;
C
CyC2018 已提交
1601
        if (cur.random != null)
C
CyC2018 已提交
1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618
            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 已提交
1619 1620
[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 已提交
1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633
## 题目描述

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

<div align="center"> <img src="../pics//79b12431-6d9d-4a7d-985b-1b79bc5bf5fb.png" width="400"/> </div><br>

## 解题思路

```java
private TreeNode pre = null;
private TreeNode head = null;

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

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

# 37. 序列化二叉树

C
CyC2018 已提交
1656 1657
[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 已提交
1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669
## 题目描述

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

## 解题思路

```java
public class Solution {

    private String deserializeStr;

    public String Serialize(TreeNode root) {
C
CyC2018 已提交
1670 1671
        if (root == null)
            return "#";
C
CyC2018 已提交
1672 1673 1674 1675 1676 1677 1678 1679 1680
        return root.val + " " + Serialize(root.left) + " " + Serialize(root.right); 
    }

    public TreeNode Deserialize(String str) {
        deserializeStr = str;
        return Deserialize();
    }

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

# 38. 字符串的排列

C
CyC2018 已提交
1699 1700
[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 已提交
1701 1702 1703 1704 1705 1706 1707 1708 1709 1710
## 题目描述

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

## 解题思路

```java
private ArrayList<String> ret = new ArrayList<>();

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

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

# 39. 数组中出现次数超过一半的数字

C
CyC2018 已提交
1740 1741
[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 已提交
1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758
## 解题思路

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

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

```java
public int MoreThanHalfNum_Solution(int[] nums) {
    int majority = nums[0];
    for (int i = 1, cnt = 1; i < nums.length; i++) {
        cnt = nums[i] == majority ? cnt + 1 : cnt - 1;
        if (cnt == 0) {
            majority = nums[i];
            cnt = 1;
        }
    }
    int cnt = 0;
C
CyC2018 已提交
1759 1760
    for (int val : nums)
        if (val == majority)
C
CyC2018 已提交
1761
            cnt++;
C
CyC2018 已提交
1762 1763 1764 1765 1766 1767
    return cnt > nums.length / 2 ? majority : 0;
}
```

# 40. 最小的 K 个数

C
CyC2018 已提交
1768 1769
[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 已提交
1770 1771 1772 1773 1774 1775 1776 1777 1778 1779
## 解题思路

### 快速选择

- 复杂度:O(N) + O(1)
- 只有当允许修改数组元素时才可以使用

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

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

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

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

C
CyC2018 已提交
1820 1821 1822 1823 1824
private void swap(int[] nums, int i, int j) {
    int t = nums[i];
    nums[i] = nums[j];
    nums[j] = t;
}
C
CyC2018 已提交
1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837
```

### 大小为 K 的最小堆

- 复杂度:O(NlogK) + O(K)
- 特别适合处理海量数据

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

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

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

# 41.1 数据流中的中位数

C
CyC2018 已提交
1853 1854
[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 已提交
1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885
## 题目描述

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

## 解题思路

```java
public class Solution {
    // 大顶堆,存储左半边元素
    private PriorityQueue<Integer> left = new PriorityQueue<>((o1, o2) -> o2 - o1);
    // 小顶堆,存储右半边元素,并且右半边元素都大于左半边
    private PriorityQueue<Integer> right = new PriorityQueue<>();
    // 当前数据流读入的元素个数
    private int N = 0;

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

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

# 41.2 字符流中第一个不重复的字符

C
CyC2018 已提交
1896 1897
[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 已提交
1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911
## 题目描述

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

## 解题思路

```java
public class Solution {
    private int[] cnts = new int[256];
    private Queue<Character> queue = new LinkedList<>();

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

    public char FirstAppearingOnce() {
C
CyC2018 已提交
1917 1918
        if (queue.isEmpty())
            return '#';
C
CyC2018 已提交
1919 1920 1921 1922 1923 1924 1925
        return queue.peek();
    }
}
```

# 42. 连续子数组的最大和

C
CyC2018 已提交
1926 1927
[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 已提交
1928 1929 1930 1931 1932 1933 1934
## 题目描述

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

## 解题思路

```java
C
CyC2018 已提交
1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945
 public int FindGreatestSumOfSubArray(int[] nums) {
     if (nums.length == 0)
         return 0;
     int ret = Integer.MIN_VALUE;
     int sum = 0;
     for (int val : nums) {
         sum = sum <= 0 ? val : sum + val;
         ret = Math.max(ret, sum);
     }
     return ret;
 }
C
CyC2018 已提交
1946 1947 1948 1949
```

# 43. 从 1 到 n 整数中 1 出现的次数

C
CyC2018 已提交
1950
[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 已提交
1951

C
CyC2018 已提交
1952
## 解题思路
C
CyC2018 已提交
1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964

```java
public int NumberOf1Between1AndN_Solution(int n) {
    int cnt = 0;
    for (int m = 1; m <= n; m *= 10) {
        int a = n / m, b = n % m;
        cnt += (a + 8) / 10 * m + (a % 10 == 1 ? b + 1 : 0);
    }
    return cnt;
}
```

C
CyC2018 已提交
1965 1966
> [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 已提交
1967 1968 1969 1970 1971 1972 1973 1974 1975 1976
# 44. 数字序列中的某一位数字

## 题目描述

数字以 0123456789101112131415... 的格式序列化到一个字符串中,求这个字符串的第 index 位。

## 解题思路

```java
public int digitAtIndex(int index) {
C
CyC2018 已提交
1977 1978
    if (index < 0)
        return -1;
C
CyC2018 已提交
1979 1980 1981 1982
    int digit = 1;
    while (true) {
        int amount = getAmountOfDigit(digit);
        int totalAmount = amount * digit;
C
CyC2018 已提交
1983
        if (index < totalAmount)
C
CyC2018 已提交
1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994
            return digitAtIndex(index, digit);
        index -= totalAmount;
        digit++;
    }
}

/**
 * digit 位数的数字组成的字符串长度
 * 例如 digit = 2,return 90
 */
private int getAmountOfDigit(int digit) {
C
CyC2018 已提交
1995 1996
    if (digit == 1)
        return 10;
C
CyC2018 已提交
1997 1998 1999 2000
    return (int) Math.pow(10, digit - 1) * 9;
}

/**
C
CyC2018 已提交
2001
 * 在 digit 位数组成的字符串中,第 index 个数
C
CyC2018 已提交
2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013
 */
private int digitAtIndex(int index, int digit) {
    int number = beginNumber(digit) + index / digit;
    int remain = index % digit;
    return (number + "").charAt(remain) - '0';
}

/**
 * digit 位数的起始数字
 * 例如 digit = 2 return 10
 */
private int beginNumber(int digit) {
C
CyC2018 已提交
2014 2015
    if (digit == 1)
        return 0;
C
CyC2018 已提交
2016 2017 2018 2019 2020 2021
    return (int) Math.pow(10, digit - 1);
}
```

# 45. 把数组排成最小的数

C
CyC2018 已提交
2022 2023
[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 已提交
2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035
## 题目描述

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

## 解题思路

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

```java
public String PrintMinNumber(int[] numbers) {
    int n = numbers.length;
    String[] nums = new String[n];
C
CyC2018 已提交
2036 2037
    for (int i = 0; i < n; i++)
        nums[i] = numbers[i] + "";
C
CyC2018 已提交
2038 2039
    Arrays.sort(nums, (s1, s2) -> (s1 + s2).compareTo(s2 + s1));
    String ret = "";
C
CyC2018 已提交
2040 2041
    for (String str : nums)
        ret += str;
C
CyC2018 已提交
2042 2043 2044 2045 2046 2047
    return ret;
}
```

# 46. 把数字翻译成字符串

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

C
CyC2018 已提交
2050 2051 2052 2053 2054 2055 2056
## 题目描述

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

## 解题思路

```java
C
CyC2018 已提交
2057
public int numDecodings(String s) {
C
CyC2018 已提交
2058 2059
    if (s == null || s.length() == 0)
        return 0;
C
CyC2018 已提交
2060 2061 2062 2063 2064 2065
    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));
C
CyC2018 已提交
2066 2067 2068 2069
        if (one != 0)
            dp[i] += dp[i - 1];
        if (s.charAt(i - 2) == '0')
            continue;
C
CyC2018 已提交
2070
        int two = Integer.valueOf(s.substring(i - 2, i));
C
CyC2018 已提交
2071 2072
        if (two <= 26)
            dp[i] += dp[i - 2];
C
CyC2018 已提交
2073
    }
C
CyC2018 已提交
2074
    return dp[n];
C
CyC2018 已提交
2075 2076 2077 2078 2079
}
```

# 47. 礼物的最大价值

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

C
CyC2018 已提交
2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099
## 题目描述

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

```
1    10   3    8
12   2    9    6
5    7    4    11
3    7    16   5
```

礼物的最大价值为 1+12+5+7+7+16+5=53。

## 解题思路

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

```java
C
CyC2018 已提交
2100
public int getMost(int[][] values) {
C
CyC2018 已提交
2101 2102
    if (values == null || values.length == 0 || values[0].length == 0)
        return 0;
C
CyC2018 已提交
2103
    int m = values.length, n = values[0].length;
C
CyC2018 已提交
2104 2105 2106
    int[] dp = new int[n];
    for (int i = 0; i < m; i++) {
        dp[0] += values[i][0];
C
CyC2018 已提交
2107
        for (int j = 1; j < n; j++)
C
CyC2018 已提交
2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125
            dp[j] = Math.max(dp[j], dp[j - 1]) + values[i][j];
    }
    return dp[n - 1];
}
```

# 48. 最长不含重复字符的子字符串

## 题目描述

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

## 解题思路

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

# 49. 丑数

C
CyC2018 已提交
2146 2147
[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 已提交
2148 2149 2150 2151 2152 2153 2154
## 题目描述

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

## 解题思路

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

# 50. 第一个只出现一次的字符位置

C
CyC2018 已提交
2177 2178
[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 已提交
2179 2180
## 题目描述

C
CyC2018 已提交
2181
在一个字符串 (1 <= 字符串长度 <= 10000,全部由字母组成) 中找到第一个只出现一次的字符,并返回它的位置。
C
CyC2018 已提交
2182 2183 2184 2185 2186 2187 2188 2189

## 解题思路

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

```java
public int FirstNotRepeatingChar(String str) {
    int[] cnts = new int[256];
C
CyC2018 已提交
2190 2191 2192 2193 2194
    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;
C
CyC2018 已提交
2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205
    return -1;
}
```

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

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

# 51. 数组中的逆序对

C
CyC2018 已提交
2222 2223
[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 已提交
2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235
## 题目描述

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

## 解题思路

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

public int InversePairs(int[] nums) {
    tmp = new int[nums.length];
C
CyC2018 已提交
2236
    mergeSort(nums, 0, nums.length - 1);
C
CyC2018 已提交
2237 2238 2239
    return (int) (cnt % 1000000007);
}

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

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

# 52. 两个链表的第一个公共结点

C
CyC2018 已提交
2271 2272
[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 已提交
2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295
## 题目描述

<div align="center"> <img src="../pics//8f6f9dc9-9ecd-47c8-b50e-2814f0219056.png" width="500"/> </div><br>

## 解题思路

设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。

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

```java
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
    ListNode l1 = pHead1, l2 = pHead2;
    while (l1 != l2) {
        l1 = (l1 == null) ? pHead2 : l1.next;
        l2 = (l2 == null) ? pHead1 : l2.next;
    }
    return l1;
}
```

# 53 数字在排序数组中出现的次数

C
CyC2018 已提交
2296 2297
[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 已提交
2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311
## 题目描述

```html
Input:
1, 2, 3, 3, 3, 3, 4, 6
3
Output:
4
```

## 解题思路

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

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

# 54. 二叉搜索树的第 K 个结点

C
CyC2018 已提交
2332 2333
[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 已提交
2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347
## 解题思路

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

```java
private TreeNode ret;
private int cnt = 0;

public TreeNode KthNode(TreeNode pRoot, int k) {
    inOrder(pRoot, k);
    return ret;
}

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

# 55.1 二叉树的深度

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

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

<div align="center"> <img src="../pics//b29f8971-9cb8-480d-b986-0e60c2ece069.png" width="350"/> </div><br>

## 解题思路

```java
public int TreeDepth(TreeNode root) {
C
CyC2018 已提交
2372
    return root == null ? 0 : 1 + Math.max(TreeDepth(root.left), TreeDepth(root.right));
C
CyC2018 已提交
2373 2374 2375 2376 2377
}
```

# 55.2 平衡二叉树

C
CyC2018 已提交
2378 2379
[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 已提交
2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396
## 题目描述

平衡二叉树左右子树高度差不超过 1。

<div align="center"> <img src="../pics//e026c24d-00fa-4e7c-97a8-95a98cdc383a.png" width="300"/> </div><br>

## 解题思路

```java
private boolean isBalanced = true;

public boolean IsBalanced_Solution(TreeNode root) {
    height(root);
    return isBalanced;
}

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

# 56. 数组中只出现一次的数字

C
CyC2018 已提交
2409 2410
[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 已提交
2411 2412
## 题目描述

C
CyC2018 已提交
2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423
一个整型数组里除了两个数字之外,其他的数字都出现了两次,找出这两个数。

## 解题思路

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

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

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

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

# 57.1 和为 S 的两个数字

C
CyC2018 已提交
2441 2442
[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 已提交
2443 2444
## 题目描述

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

## 解题思路

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

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

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

# 57.2 和为 S 的连续正数序列

C
CyC2018 已提交
2473 2474
[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 已提交
2475 2476
## 题目描述

C
CyC2018 已提交
2477 2478 2479 2480 2481 2482 2483 2484
输出所有和为 S 的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

例如和为 100 的连续序列有:

```
[9, 10, 11, 12, 13, 14, 15, 16]
[18, 19, 20, 21, 22]。
```
C
CyC2018 已提交
2485 2486 2487 2488 2489 2490

## 解题思路

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

# 58.1 翻转单词顺序列

C
CyC2018 已提交
2517 2518
[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 已提交
2519 2520 2521 2522 2523 2524 2525 2526
## 题目描述

输入:"I am a student."

输出:"student. a am I"

## 解题思路

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

正确的解法应该是和书上一样,先旋转每个单词,再旋转整个字符串。
C
CyC2018 已提交
2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547

```java
public String ReverseSentence(String str) {
    int n = str.length();
    char[] chars = str.toCharArray();
    int i = 0, j = 0;
    while (j <= n) {
        if (j == n || chars[j] == ' ') {
            reverse(chars, i, j - 1);
            i = j + 1;
        }
        j++;
    }
    reverse(chars, 0, n - 1);
    return new String(chars);
}

private void reverse(char[] c, int i, int j) {
C
CyC2018 已提交
2548
    while (i < j)
C
CyC2018 已提交
2549
        swap(c, i++, j--);
C
CyC2018 已提交
2550
}
C
CyC2018 已提交
2551 2552 2553 2554 2555 2556

private void swap(char[] c, int i, int j) {
    char t = c[i];
    c[i] = c[j];
    c[j] = t;
}
C
CyC2018 已提交
2557 2558 2559 2560
```

# 58.2 左旋转字符串

C
CyC2018 已提交
2561 2562
[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 已提交
2563 2564
## 题目描述

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

## 解题思路

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

C
CyC2018 已提交
2571 2572
```java
public String LeftRotateString(String str, int n) {
C
CyC2018 已提交
2573 2574
    if (n >= str.length())
        return str;
C
CyC2018 已提交
2575 2576 2577 2578 2579
    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 已提交
2580 2581
}

C
CyC2018 已提交
2582
private void reverse(char[] chars, int i, int j) {
C
CyC2018 已提交
2583
    while (i < j)
C
CyC2018 已提交
2584
        swap(chars, i++, j--);
C
CyC2018 已提交
2585
}
C
CyC2018 已提交
2586 2587 2588 2589 2590 2591

private void swap(char[] chars, int i, int j) {
    char t = chars[i];
    chars[i] = chars[j];
    chars[j] = t;
}
C
CyC2018 已提交
2592 2593 2594 2595
```

# 59. 滑动窗口的最大值

C
CyC2018 已提交
2596 2597
[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 已提交
2598 2599
## 题目描述

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

## 解题思路

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

# 60. n 个骰子的点数

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

C
CyC2018 已提交
2626 2627
## 题目描述

C
CyC2018 已提交
2628 2629 2630 2631 2632 2633
把 n 个骰子仍在地上,求点数和为 s 的概率。

## 解题思路

### 动态规划解法

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

C
CyC2018 已提交
2636 2637 2638
空间复杂度:O(N<sup>2</sup>)

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

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

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

### 动态规划解法 + 旋转数组

空间复杂度:O(N)

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

# 61. 扑克牌顺子

C
CyC2018 已提交
2688 2689
[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 已提交
2690 2691
## 题目描述

C
CyC2018 已提交
2692 2693 2694 2695 2696 2697
五张牌,其中大小鬼为癞子,牌面大小为 0。判断是否能组成顺子。

## 解题思路

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

# 62. 圆圈中最后剩下的数

C
CyC2018 已提交
2716 2717
[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 已提交
2718 2719
## 题目描述

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

## 解题思路

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

```java
public int LastRemaining_Solution(int n, int m) {
C
CyC2018 已提交
2728 2729 2730 2731
    if (n == 0)
        return -1;
    if (n == 1)
        return 0;
C
CyC2018 已提交
2732 2733 2734 2735 2736 2737
    return (LastRemaining_Solution(n - 1, m) + m) % n;
}
```

# 63. 股票的最大利润

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

C
CyC2018 已提交
2740 2741
## 题目描述

C
CyC2018 已提交
2742 2743 2744 2745 2746 2747 2748 2749
可以有一次买入和一次卖出,买入必须在前。求最大收益。

## 解题思路

使用贪心策略,假设第 i 轮进行卖出操作,买入操作价格应该是 i 之前并且价格最低。

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

# 64. 求 1+2+3+...+n

C
CyC2018 已提交
2765 2766
[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 已提交
2767 2768
## 题目描述

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

## 解题思路

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

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

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

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

# 65. 不用加减乘除做加法

C
CyC2018 已提交
2789 2790
[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 已提交
2791 2792
## 题目描述

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

C
CyC2018 已提交
2795 2796 2797 2798 2799 2800 2801
## 解题思路

a ^ b 表示没有考虑进位的情况下两数的和,(a & b) << 1 就是进位。

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

```java
C
CyC2018 已提交
2802 2803
public int Add(int num1,int num2) {
    return num2 == 0 ? num1 : Add(num1 ^ num2, (num1 & num2) << 1);
C
CyC2018 已提交
2804 2805 2806 2807 2808
}
```

# 66. 构建乘积数组

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

C
CyC2018 已提交
2813 2814 2815 2816 2817 2818 2819 2820
给定一个数组 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]。不能使用除法。

## 解题思路

```java
public int[] multiply(int[] A) {
    int n = A.length;
    int[] B = new int[n];
C
CyC2018 已提交
2821
    for (int i = 0, product = 1; i < n; product *= A[i], i++)
C
CyC2018 已提交
2822
        B[i] = product;
C
CyC2018 已提交
2823
    for (int i = n - 1, product = 1; i >= 0; product *= A[i], i--)
C
CyC2018 已提交
2824 2825 2826 2827 2828 2829 2830
        B[i] *= product;
    return B;
}
```

# 67. 把字符串转换成整数

C
CyC2018 已提交
2831 2832
[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 已提交
2833 2834
## 题目描述

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

```html
Iuput:
+2147483647
1a33

Output:
2147483647
0
```

C
CyC2018 已提交
2847 2848 2849 2850
## 解题思路

```java
public int StrToInt(String str) {
C
CyC2018 已提交
2851 2852
    if (str.length() == 0)
        return 0;
C
CyC2018 已提交
2853 2854 2855 2856
    char[] chars = str.toCharArray();
    boolean isNegative = chars[0] == '-';
    int ret = 0;
    for (int i = 0; i < chars.length; i++) {
C
CyC2018 已提交
2857 2858 2859 2860
        if (i == 0 && (chars[i] == '+' || chars[i] == '-'))
            continue;
        if (chars[i] < '0' || chars[i] > '9')
            return 0; // 非法输入
C
CyC2018 已提交
2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874
        ret = ret * 10 + (chars[i] - '0');
    }
    return isNegative ? -ret : ret;
}
```

# 68. 树中两个节点的最低公共祖先

## 解题思路

### 二叉查找树

<div align="center"> <img src="../pics//293d2af9-de1d-403e-bed0-85d029383528.png" width="300"/> </div><br>

C
CyC2018 已提交
2875 2876
[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 已提交
2877 2878 2879 2880
二叉查找树中,两个节点 p, q 的公共祖先 root 满足 p.val <= root.val && root.val <= q.val,只要找到满足这个条件的最低层节点即可。换句话说,应该先考虑子树的解而不是根节点的解,二叉树的后序遍历操作满足这个特性。在本题中我们可以利用后序遍历的特性,先在左右子树中查找解,最后再考虑根节点的解。

```java
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
C
CyC2018 已提交
2881 2882 2883 2884 2885 2886
    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);
C
CyC2018 已提交
2887 2888 2889 2890 2891 2892 2893 2894
    return root;
}
```

### 普通二叉树

<div align="center"> <img src="../pics//37a72755-4890-4b42-9eab-b0084e0c54d9.png" width="300"/> </div><br>

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

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

```java
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
C
CyC2018 已提交
2901 2902
    if (root == null || root == p || root == q)
        return root;
C
CyC2018 已提交
2903 2904 2905 2906 2907 2908 2909 2910 2911
    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.