diff --git a/notes/剑指 offer 题解.md b/notes/剑指 offer 题解.md
index 20963e46..7ff73846 100644
--- a/notes/剑指 offer 题解.md
+++ b/notes/剑指 offer 题解.md
@@ -440,7 +440,7 @@ public int pop() throws Exception {
如果使用递归求解,会重复计算一些子问题。例如,计算 f(10) 需要计算 f(9) 和 f(8),计算 f(9) 需要计算 f(8) 和 f(7),可以看到 f(8) 被重复计算了。
-
+
递归方法是将一个问题划分成多个子问题求解,动态规划也是如此,但是动态规划会把子问题的解缓存起来,避免重复求解子问题。
@@ -603,13 +603,15 @@ public int RectCover(int n) {
## 题目描述
-把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组 {3, 4, 5, 1, 2} 为 {1, 2, 3, 4, 5} 的一个旋转,该数组的最小值为 1。NOTE:给出的所有元素都大于 0,若数组大小为 0,请返回 0。
+把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
+
+例如数组 {3, 4, 5, 1, 2} 为 {1, 2, 3, 4, 5} 的一个旋转,该数组的最小值为 1。NOTE:给出的所有元素都大于 0,若数组大小为 0,请返回 0。
## 解题思路
当 nums[m] <= nums[h] 的情况下,说明解在 [l, m] 之间,此时令 h = m;否则解在 [m + 1, h] 之间,令 l = m + 1。
-因为 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)。
+因为 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) 二分查找部分。
复杂度:O(logN) + O(1)
@@ -732,10 +734,10 @@ private void initDigitSum() {
n /= 10;
}
}
- digitSum = new int[rows][cols];
+ this.digitSum = new int[rows][cols];
for (int i = 0; i < this.rows; i++)
for (int j = 0; j < this.cols; j++)
- digitSum[i][j] = digitSumOne[i] + digitSumOne[j];
+ this.digitSum[i][j] = digitSumOne[i] + digitSumOne[j];
}
```
@@ -747,24 +749,17 @@ private void initDigitSum() {
把一根绳子剪成多段,并且使得每段的长度乘积最大。
-For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, return 36 (10 = 3 + 3 + 4).
+```html
+n = 2
+return 1 (2 = 1 + 1)
+
+n = 10
+return 36 (10 = 3 + 3 + 4)
+```
## 解题思路
-### 动态规划解法
-
-```java
-public int integerBreak(int n) {
- int[] dp = new int[n + 1];
- dp[1] = 1;
- for (int i = 2; i <= n; i++)
- for (int j = 1; j < i; j++)
- dp[i] = Math.max(dp[i], Math.max(j * (i - j), dp[j] * (i - j)));
- return dp[n];
-}
-```
-
-### 贪心解法
+### 贪心
尽可能多剪长度为 3 的绳子,并且不允许有长度为 1 的绳子出现,如果出现了,就从已经切好长度为 3 的绳子中拿出一段与长度为 1 的绳子重新组合,把它们切成两段长度为 2 的绳子。
@@ -786,6 +781,19 @@ public int integerBreak(int n) {
}
```
+### 动态规划
+
+```java
+public int integerBreak(int n) {
+ int[] dp = new int[n + 1];
+ dp[1] = 1;
+ for (int i = 2; i <= n; i++)
+ for (int j = 1; j < i; j++)
+ dp[i] = Math.max(dp[i], Math.max(j * (i - j), dp[j] * (i - j)));
+ return dp[n];
+}
+```
+
# 15. 二进制中 1 的个数
[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)
@@ -965,28 +973,19 @@ 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" 均不匹配。
## 解题思路
应该注意到,'.' 是用来当做一个任意字符,而 '\*' 是用来重复前面的字符。这两个的作用不同,不能把 '.' 的作用和 '\*' 进行类比,从而把它当成重复前面字符一次。
-```html
-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
-```
-
```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;
for (int i = 1; i <= n; i++)
if (pattern[i - 1] == '*')
@@ -997,10 +996,13 @@ public boolean match(char[] str, char[] pattern) {
if (str[i - 1] == pattern[j - 1] || pattern[j - 1] == '.')
dp[i][j] = dp[i - 1][j - 1];
else if (pattern[j - 1] == '*')
- if (pattern[j - 2] == str[i - 1] || pattern[j - 2] == '.')
- dp[i][j] = dp[i][j - 1] || dp[i][j - 2] || dp[i - 1][j];
- else
- dp[i][j] = dp[i][j - 2];
+ if (pattern[j - 2] == str[i - 1] || pattern[j - 2] == '.') {
+ dp[i][j] |= dp[i][j - 1]; // a* counts as single a
+ dp[i][j] |= dp[i - 1][j]; // a* counts as multiple a
+ dp[i][j] |= dp[i][j - 2]; // a* counts as empty
+ } else
+ dp[i][j] = dp[i][j - 2]; // a* only counts as empty
+
return dp[m][n];
}
```
@@ -1011,10 +1013,25 @@ public boolean match(char[] str, char[] pattern) {
## 题目描述
-请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串 "+100","5e2","-123","3.1416" 和 "-1E-16" 都表示数值。 但是 "12e","1a3.14","1.2.3","+-5" 和 "12e+4.3" 都不是。
+请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。
+
+例如,字符串 "+100","5e2","-123","3.1416" 和 "-1E-16" 都表示数值。但是 "12e","1a3.14","1.2.3","+-5" 和 "12e+4.3" 都不是。
## 解题思路
+使用正则表达式进行匹配。
+
+```html
+[] : 字符集合
+() : 分组,在这里是为了让表达式更清晰
+? : 重复 0 ~ 1
++ : 重复 1 ~ n
+* : 重复 0 ~ n
+. : 任意字符
+\\. : 转义后的 .
+\\d : 任意数字
+```
+
```java
public boolean isNumeric(char[] str) {
if (str == null)
@@ -1029,7 +1046,7 @@ public boolean isNumeric(char[] str) {
## 题目描述
-保证奇数和奇数,偶数和偶数之间的相对位置不变,这和书本不太一样。
+需要保证奇数和奇数,偶数和偶数之间的相对位置不变,这和书本不太一样。
## 解题思路