diff --git a/problems/0018.四数之和.md b/problems/0018.四数之和.md index fb8557fa..f3188b0f 100644 --- a/problems/0018.四数之和.md +++ b/problems/0018.四数之和.md @@ -253,7 +253,7 @@ public class Solution { for (int k = 0; k < nums.length; k++) { // 剪枝处理 if (nums[k] > target && nums[k] >= 0) { - break; + break; // 此处的break可以等价于return result; } // 对nums[k]去重 if (k > 0 && nums[k] == nums[k - 1]) { @@ -262,7 +262,7 @@ public class Solution { for (int i = k + 1; i < nums.length; i++) { // 第二级剪枝 if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) { - break; + break; // 注意是break到上一级for循环,如果直接return result;会有遗漏 } // 对nums[i]去重 if (i > k + 1 && nums[i] == nums[i - 1]) { diff --git a/problems/0020.有效的括号.md b/problems/0020.有效的括号.md index 493e2871..2475138e 100644 --- a/problems/0020.有效的括号.md +++ b/problems/0020.有效的括号.md @@ -166,7 +166,7 @@ class Solution { deque.pop(); } } - //最后判断栈中元素是否匹配 + //遍历结束,如果栈为空,则括号全部匹配 return deque.isEmpty(); } } @@ -578,3 +578,4 @@ impl Solution { + diff --git a/problems/0383.赎金信.md b/problems/0383.赎金信.md index eb83d3ec..1d739173 100644 --- a/problems/0383.赎金信.md +++ b/problems/0383.赎金信.md @@ -104,7 +104,7 @@ public: }; ``` -* 时间复杂度: O(n) +* 时间复杂度: O(m+n),其中m表示ransomNote的长度,n表示magazine的长度 * 空间复杂度: O(1) @@ -470,3 +470,4 @@ bool canConstruct(char* ransomNote, char* magazine) { + diff --git a/problems/0459.重复的子字符串.md b/problems/0459.重复的子字符串.md index de0e6e4d..bdced0ef 100644 --- a/problems/0459.重复的子字符串.md +++ b/problems/0459.重复的子字符串.md @@ -390,6 +390,8 @@ public: ### Java: +(版本一) 前缀表 减一 + ```java class Solution { public boolean repeatedSubstringPattern(String s) { @@ -420,6 +422,45 @@ class Solution { } ``` +(版本二) 前缀表 不减一 + +```java +/* + * 充分条件:如果字符串s是由重复子串组成的,那么它的最长相等前后缀不包含的子串一定是s的最小重复子串。 + * 必要条件:如果字符串s的最长相等前后缀不包含的子串是s的最小重复子串,那么s必然是由重复子串组成的。 + * 推得:当字符串s的长度可以被其最长相等前后缀不包含的子串的长度整除时,不包含的子串就是s的最小重复子串。 + * + * 时间复杂度:O(n) + * 空间复杂度:O(n) + */ +class Solution { + public boolean repeatedSubstringPattern(String s) { + // if (s.equals("")) return false; + // 边界判断(可以去掉,因为题目给定范围是1 <= s.length <= 10^4) + int n = s.length(); + + // Step 1.构建KMP算法的前缀表 + int[] next = new int[n]; // 前缀表的值表示 以该位置结尾的字符串的最长相等前后缀的长度 + int j = 0; + next[0] = 0; + for (int i = 1; i < n; i++) { + while (j > 0 && s.charAt(i) != s.charAt(j)) // 只要前缀后缀还不一致,就根据前缀表回退j直到起点为止 + j = next[j - 1]; + if (s.charAt(i) == s.charAt(j)) + j++; + next[i] = j; + } + + // Step 2.判断重复子字符串 + if (next[n - 1] > 0 && n % (n - next[n - 1]) == 0) { // 当字符串s的长度可以被其最长相等前后缀不包含的子串的长度整除时 + return true; // 不包含的子串就是s的最小重复子串 + } else { + return false; + } + } +} +``` + ### Python: (版本一) 前缀表 减一 @@ -930,4 +971,3 @@ bool repeatedSubstringPattern(char* s) { - diff --git a/problems/二叉树的统一迭代法.md b/problems/二叉树的统一迭代法.md index 037cf110..a6d4e3ff 100644 --- a/problems/二叉树的统一迭代法.md +++ b/problems/二叉树的统一迭代法.md @@ -238,7 +238,7 @@ class Solution { while (!st.empty()) { TreeNode node = st.peek(); if (node != null) { - st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中 + st.pop(); // 将该节点弹出,避免重复操作,下面再将右左中节点添加到栈中(前序遍历-中左右,入栈顺序右左中) if (node.right!=null) st.push(node.right); // 添加右节点(空节点不入栈) if (node.left!=null) st.push(node.left); // 添加左节点(空节点不入栈) st.push(node); // 添加中节点 @@ -266,11 +266,10 @@ public List inorderTraversal(TreeNode root) { while (!st.empty()) { TreeNode node = st.peek(); if (node != null) { - st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中 + st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中(中序遍历-左中右,入栈顺序右中左) if (node.right!=null) st.push(node.right); // 添加右节点(空节点不入栈) st.push(node); // 添加中节点 st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。 - if (node.left!=null) st.push(node.left); // 添加左节点(空节点不入栈) } else { // 只有遇到空节点的时候,才将下一个节点放进结果集 st.pop(); // 将空节点弹出 @@ -294,7 +293,7 @@ class Solution { while (!st.empty()) { TreeNode node = st.peek(); if (node != null) { - st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中 + st.pop(); // 将该节点弹出,避免重复操作,下面再将中右左节点添加到栈中(后序遍历-左右中,入栈顺序中右左) st.push(node); // 添加中节点 st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。 if (node.right!=null) st.push(node.right); // 添加右节点(空节点不入栈) @@ -975,3 +974,4 @@ public IList PostorderTraversal(TreeNode root) +