diff --git a/notes/剑指 offer 题解.md b/notes/剑指 offer 题解.md
index 4851618c..c0f792fb 100644
--- a/notes/剑指 offer 题解.md
+++ b/notes/剑指 offer 题解.md
@@ -461,6 +461,8 @@ public class Solution {
一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
+## 解题思路
+
```java
public int JumpFloor(int n) {
if (n == 1) return 1;
@@ -480,6 +482,8 @@ public int JumpFloor(int n) {
一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级……它也可以跳上 n 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
+## 解题思路
+
```java
public int JumpFloorII(int n) {
int[] dp = new int[n];
@@ -499,6 +503,8 @@ public int JumpFloorII(int n) {
我们可以用 2\*1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2\*1 的小矩形无重叠地覆盖一个 2\*n 的大矩形,总共有多少种方法?
+## 解题思路
+
```java
public int RectCover(int n) {
if (n < 2) return n;
@@ -685,7 +691,7 @@ int maxProductAfterCuttin(int length) {
输入一个整数,输出该数二进制表示中 1 的个数。其中负数用补码表示
-**使用库函数**
+### Integer.bitCount()
```java
public int NumberOf1(int n) {
@@ -693,9 +699,11 @@ public int NumberOf1(int n) {
}
```
-**O(logM) 时间复杂度解法,其中 M 表示 1 的个数**
+### n&(n-1)
-n&(n-1) 该位运算是去除 n 的位级表示中最低的那一位。例如对于二进制表示 10110100,减去 1 得到 10110011,这两个数相与得到 10110000。
+O(logM) 时间复杂度解法,其中 M 表示 1 的个数。
+
+该位运算是去除 n 的位级表示中最低的那一位。例如对于二进制表示 10110100,减去 1 得到 10110011,这两个数相与得到 10110000。
```java
public int NumberOf1(int n) {
@@ -782,16 +790,16 @@ private void printNumber(char[] number) {
## 解题思路
-- 如果链表不是尾节点,那么可以直接将下一个节点的值赋给节点,令节点指向下下个节点,然后删除下一个节点,时间复杂度为 O(1)。
+① 如果链表不是尾节点,那么可以直接将下一个节点的值赋给节点,令节点指向下下个节点,然后删除下一个节点,时间复杂度为 O(1)。
-- 否则,就需要先遍历链表,找到节点的前一个节点,然后让前一个节点指向节点的下一个节点,时间复杂度为 O(N)。
+② 否则,就需要先遍历链表,找到节点的前一个节点,然后让前一个节点指向节点的下一个节点,时间复杂度为 O(N)。
-- 综上,如果进行 N 次操作,那么大约需要移动节点的次数为 N-1+N=2N-1,其中 N-1 表示不是链表尾节点情况下的移动次数,N 表示是尾节点情况下的移动次数。(2N-1)/N \~ 2,因此该算法的时间复杂度为 O(1)。
+③ 综上,如果进行 N 次操作,那么大约需要移动节点的次数为 N-1+N=2N-1,其中 N-1 表示不是链表尾节点情况下的移动次数,N 表示是尾节点情况下的移动次数。(2N-1)/N \~ 2,因此该算法的时间复杂度为 O(1)。
```java
public ListNode deleteNode(ListNode head, ListNode tobeDelete) {
@@ -819,9 +827,8 @@ Input : 1->2->3->3->4->4->5
Output : 1->2->5
```
-**解题描述**
+## 解题描述
-递归。
```java
public ListNode deleteDuplication(ListNode pHead) {
@@ -844,7 +851,9 @@ public ListNode deleteDuplication(ListNode pHead) {
## 题目描述
-请实现一个函数用来匹配包括 '.' 和 '\*' 的正则表达式。模式中的字符 '.' 表示任意一个字符,而 '\*' 表示它前面的字符可以出现任意次(包含 0 次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串 "aaa" 与模式 "a.a" 和 "ab\*ac\*a" 匹配,但是与 "aa.a" 和 "ab\*a" 均不匹配.
+请实现一个函数用来匹配包括 '.' 和 '\*' 的正则表达式。模式中的字符 '.' 表示任意一个字符,而 '\*' 表示它前面的字符可以出现任意次(包含 0 次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串 "aaa" 与模式 "a.a" 和 "ab\*ac\*a" 匹配,但是与 "aa.a" 和 "ab\*a" 均不匹配。
+
+## 解题思路
```java
public boolean match(char[] s, char[] p) {
@@ -879,6 +888,8 @@ public boolean match(char[] s, char[] p) {
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串 "+100","5e2","-123","3.1416" 和 "-1E-16" 都表示数值。 但是 "12e","1a3.14","1.2.3","+-5" 和 "12e+4.3" 都不是。
+## 解题思路
+
```java
public boolean isNumeric(char[] str) {
String string = String.valueOf(str);
@@ -888,14 +899,13 @@ public boolean isNumeric(char[] str) {
# 21. 调整数组顺序使奇数位于偶数前面
-**题目要求**
+## 题目描述
保证奇数和奇数,偶数和偶数之间的相对位置不变,这和书本不太一样。
## 解题思路
-- 时间复杂度 : O(n2)
-- 空间复杂度 : O(1)
+复杂度:O(n2) + O(1)
```java
public void reOrderArray(int[] array) {
@@ -915,8 +925,7 @@ public void reOrderArray(int[] array) {
}
```
-- 时间复杂度 : O(n)
-- 空间复杂度 : O(n)
+复杂度:O(n) + O(n)
```java
public void reOrderArray(int[] array) {
@@ -937,6 +946,8 @@ public void reOrderArray(int[] array) {
设链表的长度为 N。设两个指针 P1 和 P2,先让 P1 移动 K 个节点,则还有 N - K 个节点可以移动。此时让 P1 和 P2 同时移动,可以知道当 P1 移动到链表结尾时,P2 移动到 N - K 个节点处,该位置就是倒数第 K 个节点。
+## 解题思路
+
```java
@@ -988,8 +999,6 @@ public ListNode EntryNodeOfLoop(ListNode pHead) {
## 解题思路
-头插法
-
```java
public ListNode ReverseList(ListNode head) {
ListNode newList = new ListNode(-1);
@@ -1009,6 +1018,8 @@ public ListNode ReverseList(ListNode head) {
+## 解题思路
+
```java
public ListNode Merge(ListNode list1, ListNode list2) {
ListNode head = new ListNode(-1);
@@ -1035,6 +1046,8 @@ public ListNode Merge(ListNode list1, ListNode list2) {
+## 解题思路
+
```java
public boolean HasSubtree(TreeNode root1, TreeNode root2) {
if (root1 == null || root2 == null) return false;
@@ -1056,6 +1069,8 @@ private boolean isSubtree(TreeNode root1, TreeNode root2) {
+## 解题思路
+
```java
public void Mirror(TreeNode root) {
if (root == null) return;
@@ -1079,6 +1094,8 @@ public void Mirror(TreeNode root) {
3 4 4 3
```
+## 解题思路
+
```java
boolean isSymmetrical(TreeNode pRoot) {
if (pRoot == null) return true;
@@ -1107,6 +1124,8 @@ boolean isSymmetrical(TreeNode t1, TreeNode t2) {
平衡二叉树左右子树高度差不超过 1。
+## 解题思路
+
```java
private boolean isBalanced = true;
@@ -1132,6 +1151,8 @@ private int height(TreeNode root) {
+## 解题思路
+
```java
public ArrayList printMatrix(int[][] matrix) {
ArrayList ret = new ArrayList<>();
@@ -1153,6 +1174,8 @@ public ArrayList printMatrix(int[][] matrix) {
定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的 min 函数。
+## 解题思路
+
```java
private Stack stack = new Stack<>();
private Stack minStack = new Stack<>();
@@ -1185,6 +1208,8 @@ public int min() {
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列 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;
@@ -1241,6 +1266,8 @@ public ArrayList PrintFromTopToBottom(TreeNode root) {
和上题几乎一样。
+## 解题思路
+
```java
ArrayList> Print(TreeNode pRoot) {
ArrayList> ret = new ArrayList<>();
@@ -1268,6 +1295,8 @@ ArrayList> Print(TreeNode pRoot) {
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
+## 解题思路
+
```java
public ArrayList> Print(TreeNode pRoot) {
ArrayList> ret = new ArrayList<>();
@@ -1306,6 +1335,8 @@ public ArrayList> Print(TreeNode pRoot) {
+## 解题思路
+
```java
public boolean VerifySquenceOfBST(int[] sequence) {
if (sequence.length == 0) return false;
@@ -1334,6 +1365,8 @@ private boolean verify(int[] sequence, int first, int last) {
+## 解题思路
+
```java
private ArrayList> ret = new ArrayList<>();
@@ -1420,6 +1453,8 @@ public RandomListNode Clone(RandomListNode pHead) {
+## 解题思路
+
```java
private TreeNode pre = null;
public TreeNode Convert(TreeNode pRootOfTree) {
@@ -1445,6 +1480,8 @@ private void inOrder(TreeNode node) {
请实现两个函数,分别用来序列化和反序列化二叉树。
+## 解题思路
+
```java
public class Solution {
@@ -1481,6 +1518,8 @@ public class Solution {
输入一个字符串 , 按字典序打印出该字符串中字符的所有排列。例如输入字符串 abc, 则打印出由字符 a, b, c 所能排列出来的所有字符串 abc, acb, bac, bca, cab 和 cba。
+## 解题思路
+
```java
private ArrayList ret = new ArrayList<>();
@@ -1538,10 +1577,11 @@ public int MoreThanHalfNum_Solution(int[] nums) {
# 40. 最小的 K 个数
-**快速选择**
+## 解题思路
-- 时间复杂度:O(N)
-- 空间复杂度:O(1)
+### 快速选择
+
+- 复杂度:O(N) + O(1)
- 只有当可以修改数组元素时才可以使用
快速排序的 partition() 方法,会返回一个整数 j 使得 a[lo..j-1] 小于等于 a[j],且 a[j+1..hi] 大于等于 a[j],此时 a[j] 就是数组的第 j 大元素,可以利用这个特性找出数组的第 K 个元素,这种找第 K 个元素的算法称为快速选择算法。
@@ -1601,10 +1641,9 @@ private boolean less(int v, int w) {
}
```
-**大小为 K 的最小堆**
+### 大小为 K 的最小堆
-- 时间复杂度:O(NlogK)
-- 空间复杂度:O(K)
+- 复杂度:O(NlogK) + O(K)
- 特别适合处理海量数据
应该注意的是,应该使用大顶堆来维护最小堆,而不能直接创建一个小顶堆并设置一个大小,企图让小顶堆中的元素都是最小元素。
@@ -1632,6 +1671,8 @@ public ArrayList GetLeastNumbers_Solution(int[] input, int k) {
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
+## 解题思路
+
```java
public class Solution {
// 大顶堆,存储左半边元素
@@ -1672,6 +1713,8 @@ public class Solution {
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符 "go" 时,第一个只出现一次的字符是 "g"。当从该字符流中读出前六个字符“google" 时,第一个只出现一次的字符是 "l"。
+## 解题思路
+
```java
public class Solution {
private int[] cnts = new int[256];
@@ -1696,7 +1739,9 @@ public class Solution {
## 题目描述
-{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为 8(从第 0 个开始,到第 3 个为止)
+{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为 8(从第 0 个开始,到第 3 个为止)。
+
+## 解题思路
```java
public int FindGreatestSumOfSubArray(int[] nums) {
@@ -1733,6 +1778,8 @@ public int NumberOf1Between1AndN_Solution(int n) {
数字以 0123456789101112131415... 的格式序列化到一个字符串中,求这个字符串的第 index 位。
+## 解题思路
+
```java
public int digitAtIndex(int index) {
if (index < 0) return -1;
@@ -1804,6 +1851,8 @@ public String PrintMinNumber(int[] numbers) {
给定一个数字,按照如下规则翻译成字符串:0 翻译成“a”,1 翻译成“b”...25 翻译成“z”。一个数字有多种翻译可能,例如 12258 一共有 5 种,分别是 bccfi,bwfi,bczi,mcfi,mzi。实现一个函数,用来计算一个数字有多少种不同的翻译方法。
+## 解题思路
+
```java
public int getTranslationCount(String number) {
int n = number.length();
@@ -1861,6 +1910,8 @@ public int getMaxValue(int[][] values) {
输入一个字符串(只包含 a\~z 的字符),求其最长不含重复字符的子字符串的长度。例如对于 arabcacfr,最长不含重复字符的子字符串为 acfr,长度为 4。
+## 解题思路
+
```java
public int longestSubStringWithoutDuplication(String str) {
int curLen = 0;
@@ -1887,6 +1938,8 @@ public int longestSubStringWithoutDuplication(String str) {
把只包含因子 2、3 和 5 的数称作丑数(Ugly Number)。例如 6、8 都是丑数,但 14 不是,因为它包含因子 7。 习惯上我们把 1 当做是第一个丑数。求按从小到大的顺序的第 N 个丑数。
+## 解题思路
+
```java
public int GetUglyNumber_Solution(int N) {
if (N <= 6) return N;
@@ -1923,6 +1976,8 @@ public int FirstNotRepeatingChar(String str) {
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数 P。
+## 解题思路
+
```java
private long cnt = 0;
private int[] tmp; // 在这里创建辅助数组,而不是在 merge() 递归函数中创建
@@ -2123,7 +2178,9 @@ public ArrayList FindNumbersWithSum(int[] array, int sum) {
## 题目描述
-和为 100 的连续序列有 18, 19, 20, 21, 22
+和为 100 的连续序列有 18, 19, 20, 21, 22。
+
+## 解题思路
```java
public ArrayList> FindContinuousSequence(int sum) {
@@ -2200,6 +2257,8 @@ private void reverse(char[] c, int start, int end) {
对于一个给定的字符序列 S,请你把其循环左移 K 位后的序列输出。例如,字符序列 S=”abcXYZdef”, 要求输出循环左移 3 位后的结果,即“XYZdefabc”。
+## 解题思路
+
```java
public String LeftRotateString(String str, int k) {
if (str.length() == 0) return "";
@@ -2225,7 +2284,9 @@ private void reverse(char[] c, int i, int j) {
## 题目描述
-给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组 {2, 3, 4, 2, 6, 2, 5, 1} 及滑动窗口的大小 3,那么一共存在 6 个滑动窗口,他们的最大值分别为 {4, 4, 6, 6, 6, 5};
+给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组 {2, 3, 4, 2, 6, 2, 5, 1} 及滑动窗口的大小 3,那么一共存在 6 个滑动窗口,他们的最大值分别为 {4, 4, 6, 6, 6, 5}。
+
+## 解题思路
```java
public ArrayList maxInWindows(int[] num, int size) {
@@ -2250,7 +2311,11 @@ public ArrayList maxInWindows(int[] num, int size) {
把 n 个骰子仍在地上,求点数和为 s 的概率。
-最直观的动态规划解法,O(n2) 的空间复杂度。
+## 解题思路
+
+### 动态规划解法
+
+空间复杂度:O(n2)
```java
private static int face = 6;
@@ -2276,7 +2341,9 @@ public double countProbability(int n, int s) {
}
```
-使用旋转数组将空间复杂度降低为 O(n)
+### 动态规划解法 + 旋转数组
+
+空间复杂度:O(n)
```java
private static int face = 6;
@@ -2310,6 +2377,8 @@ public double countProbability(int n, int s) {
五张牌,其中大小鬼为癞子,牌面大小为 0。判断是否能组成顺子。
+## 解题思路
+
```java
public boolean isContinuous(int [] numbers) {
if(numbers.length < 5) return false;
@@ -2350,6 +2419,8 @@ public int LastRemaining_Solution(int n, int m) {
可以有一次买入和一次卖出,买入必须在前。求最大收益。
+## 解题思路
+
```java
public int maxProfit(int[] prices) {
int n = prices.length;
@@ -2368,7 +2439,9 @@ public int maxProfit(int[] prices) {
## 题目描述
-求 1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case 等关键字及条件判断语句(A?B:C)
+求 1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case 等关键字及条件判断语句(A?B:C)。
+
+## 解题思路
```java
public int Sum_Solution(int n) {
@@ -2395,6 +2468,8 @@ public int Add(int num1, int num2) {
给定一个数组 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;