diff --git a/problems/0015.三数之和.md b/problems/0015.三数之和.md index ae218385..f7146907 100644 --- a/problems/0015.三数之和.md +++ b/problems/0015.三数之和.md @@ -403,6 +403,7 @@ class Solution: ``` ### Go: +(版本一) 双指针 ```Go func threeSum(nums []int) [][]int { @@ -442,6 +443,42 @@ func threeSum(nums []int) [][]int { return res } ``` +(版本二) 哈希解法 + +```Go +func threeSum(nums []int) [][]int { + res := make([][]int, 0) + sort.Ints(nums) + // 找出a + b + c = 0 + // a = nums[i], b = nums[j], c = -(a + b) + for i := 0; i < len(nums); i++ { + // 排序之后如果第一个元素已经大于零,那么不可能凑成三元组 + if nums[i] > 0 { + break + } + // 三元组元素a去重 + if i > 0 && nums[i] == nums[i-1] { + continue + } + set := make(map[int]struct{}) + for j := i + 1; j < len(nums); j++ { + // 三元组元素b去重 + if j > i + 2 && nums[j] == nums[j-1] && nums[j-1] == nums[j-2] { + continue + } + c := -nums[i] - nums[j] + if _, ok := set[c]; ok { + res = append(res, []int{nums[i], nums[j], c}) + // 三元组元素c去重 + delete(set, c) + } else { + set[nums[j]] = struct{}{} + } + } + } + return res +} +``` ### JavaScript: diff --git a/problems/0054.螺旋矩阵.md b/problems/0054.螺旋矩阵.md index 022eed66..6c0bad40 100644 --- a/problems/0054.螺旋矩阵.md +++ b/problems/0054.螺旋矩阵.md @@ -200,6 +200,66 @@ class Solution { } ``` +```java +class Solution { + public List spiralOrder(int[][] matrix) { + List res = new ArrayList<>(); // 存放结果 + if (matrix.length == 0 || matrix[0].length == 0) + return res; + int rows = matrix.length, columns = matrix[0].length; + int startx = 0, starty = 0; // 定义每循环一个圈的起始位置 + int loop = 0; // 循环次数 + int offset = 1; // 每一圈循环,需要控制每一条边遍历的长度 + while (loop < Math.min(rows, columns) / 2) { + int i = startx; + int j = starty; + // 模拟填充上行从左到右(左闭右开) + for (; j < columns - offset; j++) { + res.add(matrix[i][j]); + } + // 模拟填充右列从上到下(左闭右开) + for (; i < rows - offset; i++) { + res.add(matrix[i][j]); + } + // 模拟填充下行从右到左(左闭右开) + for (; j > starty; j--) { + res.add(matrix[i][j]); + } + // 模拟填充左列从下到上(左闭右开) + for (; i > startx; i--) { + res.add(matrix[i][j]); + } + + // 起始位置加1 循环次数加1 并控制每条边遍历的长度 + startx++; + starty++; + offset++; + loop++; + } + + // 如果列或行中的最小值为奇数 则一定有未遍历的部分 + // 可以自行画图理解 + if (Math.min(rows, columns) % 2 == 1) { + // 当行大于列时 未遍历的部分是列 + // (startx, starty)即下一个要遍历位置 从该位置出发 遍历完未遍历的列 + // 遍历次数为rows - columns + 1 + if (rows > columns) { + for (int i = 0; i < rows - columns + 1; i++) { + res.add(matrix[startx++][starty]); + } + } else { + // 此处与上面同理 遍历完未遍历的行 + for (int i = 0; i < columns - rows + 1; i++) { + res.add(matrix[startx][starty++]); + } + } + } + + return res; + } +} +``` + ### Javascript ``` /** diff --git a/problems/0102.二叉树的层序遍历.md b/problems/0102.二叉树的层序遍历.md index 421c5dd9..17832e44 100644 --- a/problems/0102.二叉树的层序遍历.md +++ b/problems/0102.二叉树的层序遍历.md @@ -201,7 +201,7 @@ class Solution: return result ``` ```python -# 递归法 +#递归法 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): @@ -210,18 +210,24 @@ class Solution: # self.right = right class Solution: def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + if not root: + return [] + levels = [] - self.helper(root, 0, levels) + + def traverse(node, level): + if not node: + return + + if len(levels) == level: + levels.append([]) + + levels[level].append(node.val) + traverse(node.left, level + 1) + traverse(node.right, level + 1) + + traverse(root, 0) return levels - - def helper(self, node, level, levels): - if not node: - return - if len(levels) == level: - levels.append([]) - levels[level].append(node.val) - self.helper(node.left, level + 1, levels) - self.helper(node.right, level + 1, levels) ``` diff --git a/problems/0142.环形链表II.md b/problems/0142.环形链表II.md index d97b160b..7cda58c3 100644 --- a/problems/0142.环形链表II.md +++ b/problems/0142.环形链表II.md @@ -26,7 +26,7 @@ ## 算法公开课 -**[《代码随想录》算法视频公开课](https://programmercarl.com/other/gongkaike.html):[把环形链表讲清楚!| LeetCode:142.环形链表II](https://www.bilibili.com/video/BV1if4y1d7ob),相信结合视频在看本篇题解,更有助于大家对链表的理解。** +**[《代码随想录》算法视频公开课](https://programmercarl.com/other/gongkaike.html):[把环形链表讲清楚!| LeetCode:142.环形链表II](https://www.bilibili.com/video/BV1if4y1d7ob),相信结合视频再看本篇题解,更有助于大家对链表的理解。** ## 思路 diff --git a/problems/0454.四数相加II.md b/problems/0454.四数相加II.md index db9a9e43..83dea97e 100644 --- a/problems/0454.四数相加II.md +++ b/problems/0454.四数相加II.md @@ -54,7 +54,7 @@ 1. 首先定义 一个unordered_map,key放a和b两数之和,value 放a和b两数之和出现的次数。 2. 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中。 3. 定义int变量count,用来统计 a+b+c+d = 0 出现的次数。 -4. 在遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就用count把map中key对应的value也就是出现次数统计出来。 +4. 再遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就用count把map中key对应的value也就是出现次数统计出来。 5. 最后返回统计值 count 就可以了 C++代码: @@ -71,7 +71,7 @@ public: } } int count = 0; // 统计a+b+c+d = 0 出现的次数 - // 在遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。 + // 再遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。 for (int c : C) { for (int d : D) { if (umap.find(0 - (c + d)) != umap.end()) { diff --git a/problems/0459.重复的子字符串.md b/problems/0459.重复的子字符串.md index 1028bf1e..51425796 100644 --- a/problems/0459.重复的子字符串.md +++ b/problems/0459.重复的子字符串.md @@ -403,6 +403,18 @@ func repeatedSubstringPattern(s string) bool { } ``` +移动匹配 + +```go +func repeatedSubstringPattern(s string) bool { + if len(s) == 0 { + return false + } + t := s + s + return strings.Contains(t[1:len(t)-1], s) +} +``` + ### JavaScript: > 前缀表统一减一 diff --git a/problems/1020.飞地的数量.md b/problems/1020.飞地的数量.md index 59610c68..ff59411e 100644 --- a/problems/1020.飞地的数量.md +++ b/problems/1020.飞地的数量.md @@ -395,7 +395,7 @@ class Solution { 深度优先遍历 -```Python3 +```Python class Solution: def __init__(self): self.position = [[-1, 0], [0, 1], [1, 0], [0, -1]] # 四个方向 @@ -442,7 +442,7 @@ class Solution: 广度优先遍历 -```Python3 +```Python class Solution: def __init__(self): self.position = [[-1, 0], [0, 1], [1, 0], [0, -1]] # 四个方向 diff --git a/problems/1971.寻找图中是否存在路径.md b/problems/1971.寻找图中是否存在路径.md index 24e81992..2c5901ae 100644 --- a/problems/1971.寻找图中是否存在路径.md +++ b/problems/1971.寻找图中是否存在路径.md @@ -38,11 +38,22 @@ void init() { father[i] = i; } } -// 并查集里寻根的过程 +// 并查集里寻根的过程,这里递归调用当题目数据过多,递归调用可能会发生栈溢出 + int find(int u) { return u == father[u] ? u : father[u] = find(father[u]); // 路径压缩 } +// 使用迭代的方法可以避免栈溢出问题 +int find(int x) { + while (x != parent[x]) { + // 路径压缩,直接将x链接到其祖先节点,减少树的高度 + parent[x] = parent[parent[x]]; + x = parent[x]; + } +return x; +} + // 判断 u 和 v是否找到同一个根 bool isSame(int u, int v) { u = find(u); @@ -75,6 +86,8 @@ void join(int u, int v) { 此时我们就可以直接套用并查集模板。 +本题在join函数调用find函数时如果是递归调用会发生栈溢出提示,建议使用迭代方法 + 使用 join(int u, int v)将每条边加入到并查集。 最后 isSame(int u, int v) 判断是否是同一个根 就可以了。 @@ -93,8 +106,13 @@ private: } } // 并查集里寻根的过程 - int find(int u) { - return u == father[u] ? u : father[u] = find(father[u]); + int find(int x) { + while (x != parent[x]) { + // 路径压缩,直接将x链接到其祖先节点,减少树的高度 + parent[x] = parent[parent[x]]; + x = parent[x]; + } + return x; } // 判断 u 和 v是否找到同一个根 diff --git a/problems/kamacoder/0055.右旋字符串.md b/problems/kamacoder/0055.右旋字符串.md index 22be955f..4dea19a8 100644 --- a/problems/kamacoder/0055.右旋字符串.md +++ b/problems/kamacoder/0055.右旋字符串.md @@ -295,7 +295,37 @@ int main() ``` ### JavaScript: +```javascript +// JS中字符串内不可单独修改 +// 右旋转 +function reverseLeftWords(s, k) { + const reverse = (sList, start, end) => { + for (let i = start, j = end; i < j; i++, j--) { + [sList[i], sList[j]] = [sList[j], sList[i]]; + } + } + const sList = Array.from(s); + reverse(sList, 0, sList.length - k - 1); + reverse(sList, sList.length - k, sList.length - 1); + reverse(sList, 0, sList.length - 1); + return sList.join(''); +} + +// 左旋转 +var reverseLeftWords = function(s, n) { + const reverse = (sList, start, end) => { + for (let i = start, j = end; i < j; i++, j--) { + [sList[i], sList[j]] = [sList[j], sList[i]]; + } + } + const sList = s.split(''); + reverse(sList, 0, n - 1); + reverse(sList, n, sList.length - 1); + reverse(sList, 0, sList.length - 1); + return sList.join(''); +}; +``` ### TypeScript: diff --git a/problems/二叉树的迭代遍历.md b/problems/二叉树的迭代遍历.md index 8549bac1..5f59c388 100644 --- a/problems/二叉树的迭代遍历.md +++ b/problems/二叉树的迭代遍历.md @@ -117,7 +117,7 @@ public: ### 后序遍历(迭代法) -再来看后序遍历,先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了,如下图: +再来看后序遍历,先序遍历是中左右,后序遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了,如下图: ![前序到后序](https://code-thinking-1253855093.file.myqcloud.com/pics/20200808200338924.png) @@ -153,7 +153,7 @@ public: 上面这句话,可能一些同学不太理解,建议自己亲手用迭代法,先写出来前序,再试试能不能写出中序,就能理解了。 -**那么问题又来了,难道 二叉树前后中序遍历的迭代法实现,就不能风格统一么(即前序遍历 改变代码顺序就可以实现中序 和 后序)?** +**那么问题又来了,难道二叉树前后中序遍历的迭代法实现,就不能风格统一么(即前序遍历改变代码顺序就可以实现中序 和 后序)?** 当然可以,这种写法,还不是很好理解,我们将在下一篇文章里重点讲解,敬请期待!