diff --git a/problems/0031.下一个排列.md b/problems/0031.下一个排列.md index 88fbb2fc..34aa1086 100644 --- a/problems/0031.下一个排列.md +++ b/problems/0031.下一个排列.md @@ -168,7 +168,8 @@ class Solution: Do not return anything, modify nums in-place instead. """ length = len(nums) - for i in range(length - 1, -1, -1): + for i in range(length - 2, -1, -1): # 从倒数第二个开始 + if nums[i]>=nums[i+1]: continue # 剪枝去重 for j in range(length - 1, i, -1): if nums[j] > nums[i]: nums[j], nums[i] = nums[i], nums[j] diff --git a/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md b/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md index c7ff6dce..7e58a870 100644 --- a/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md +++ b/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md @@ -355,8 +355,8 @@ class Solution: while left <= right: middle = left + (right-left) // 2 if nums[middle] >= target: # 寻找左边界,nums[middle] == target的时候更新right - right = middle - 1; - leftBoder = right; + right = middle - 1 + leftBoder = right else: left = middle + 1 return leftBoder diff --git a/problems/0042.接雨水.md b/problems/0042.接雨水.md index e8dd3690..ac6f20f9 100644 --- a/problems/0042.接雨水.md +++ b/problems/0042.接雨水.md @@ -471,7 +471,7 @@ class Solution { ### Python: 双指针法 -```python3 +```Python class Solution: def trap(self, height: List[int]) -> int: res = 0 @@ -510,7 +510,7 @@ class Solution: return result ``` 单调栈 -```python3 +```Python class Solution: def trap(self, height: List[int]) -> int: # 单调栈 diff --git a/problems/0045.跳跃游戏II.md b/problems/0045.跳跃游戏II.md index 05ad872b..7dbf531b 100644 --- a/problems/0045.跳跃游戏II.md +++ b/problems/0045.跳跃游戏II.md @@ -73,11 +73,11 @@ public: for (int i = 0; i < nums.size(); i++) { nextDistance = max(nums[i] + i, nextDistance); // 更新下一步覆盖最远距离下标 if (i == curDistance) { // 遇到当前覆盖最远距离下标 - if (curDistance != nums.size() - 1) { // 如果当前覆盖最远距离下标不是终点 + if (curDistance < nums.size() - 1) { // 如果当前覆盖最远距离下标不是终点 ans++; // 需要走下一步 curDistance = nextDistance; // 更新当前覆盖最远距离下标(相当于加油了) if (nextDistance >= nums.size() - 1) break; // 下一步的覆盖范围已经可以达到终点,结束循环 - } else break; // 当前覆盖最远距离下标是集合终点,不用做ans++操作了,直接结束 + } else break; // 当前覆盖最远距到达集合终点,不用做ans++操作了,直接结束 } } return ans; @@ -126,7 +126,7 @@ public: 可以看出版本二的代码相对于版本一简化了不少! -其精髓在于控制移动下标i只移动到nums.size() - 2的位置,所以移动下标只要遇到当前覆盖最远距离的下标,直接步数加一,不用考虑别的了。 +**其精髓在于控制移动下标i只移动到nums.size() - 2的位置**,所以移动下标只要遇到当前覆盖最远距离的下标,直接步数加一,不用考虑别的了。 ## 总结 diff --git a/problems/0053.最大子序和.md b/problems/0053.最大子序和.md index 665710a9..14017f98 100644 --- a/problems/0053.最大子序和.md +++ b/problems/0053.最大子序和.md @@ -12,9 +12,9 @@ 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 示例: -输入: [-2,1,-3,4,-1,2,1,-5,4] -输出: 6 -解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 +* 输入: [-2,1,-3,4,-1,2,1,-5,4] +* 输出: 6 +* 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 ## 暴力解法 @@ -103,8 +103,28 @@ public: 当然题目没有说如果数组为空,应该返回什么,所以数组为空的话返回啥都可以了。 + +## 常见误区 + +误区一: + 不少同学认为 如果输入用例都是-1,或者 都是负数,这个贪心算法跑出来的结果是0, 这是**又一次证明脑洞模拟不靠谱的经典案例**,建议大家把代码运行一下试一试,就知道了,也会理解 为什么 result 要初始化为最小负数了。 + +误区二: + +大家在使用贪心算法求解本题,经常陷入的误区,就是分不清,是遇到 负数就选择起始位置,还是连续和为负选择起始位置。 + +在动画演示用,大家可以发现, 4,遇到 -1 的时候,我们依然累加了,为什么呢? + +因为和为3,只要连续和还是正数就会 对后面的元素 起到增大总和的作用。 所以只要连续和为正数我们就保留。 + +这里也会有录友疑惑,那 4 + -1 之后 不就变小了吗? 会不会错过 4 成为最大连续和的可能性? + +其实并不会,因为还有一个变量result 一直在更新 最大的连续和,只要有更大的连续和出现,result就更新了,那么result已经把4更新了,后面 连续和变成3,也不会对最后结果有影响。 + + + ## 动态规划 当然本题还可以用动态规划来做,当前[「代码随想录」](https://img-blog.csdnimg.cn/20201124161234338.png)主要讲解贪心系列,后续到动态规划系列的时候会详细讲解本题的dp方法。 @@ -135,7 +155,7 @@ public: 本题的贪心思路其实并不好想,这也进一步验证了,别看贪心理论很直白,有时候看似是常识,但贪心的题目一点都不简单! -后续将介绍的贪心题目都挺难的,哈哈,所以贪心很有意思,别小看贪心! +后续将介绍的贪心题目都挺难的,所以贪心很有意思,别小看贪心! ## 其他语言版本 diff --git a/problems/0055.跳跃游戏.md b/problems/0055.跳跃游戏.md index 7584e952..7b02075b 100644 --- a/problems/0055.跳跃游戏.md +++ b/problems/0055.跳跃游戏.md @@ -78,7 +78,7 @@ public: 一些同学可能感觉,我在讲贪心系列的时候,题目和题目之间貌似没有什么联系? -**是真的就是没什么联系,因为贪心无套路!**没有个整体的贪心框架解决一系列问题,只能是接触各种类型的题目锻炼自己的贪心思维! +**是真的就是没什么联系,因为贪心无套路**!没有个整体的贪心框架解决一系列问题,只能是接触各种类型的题目锻炼自己的贪心思维! ## 其他语言版本 diff --git a/problems/0093.复原IP地址.md b/problems/0093.复原IP地址.md index 97178cd5..25d9e84b 100644 --- a/problems/0093.复原IP地址.md +++ b/problems/0093.复原IP地址.md @@ -40,6 +40,9 @@ * 0 <= s.length <= 3000 * s 仅由数字组成 +# 算法公开课 + +**《代码随想录》算法视频公开课:[93.复原IP地址](https://www.bilibili.com/video/BV1XP4y1U73i/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 # 思路 @@ -424,6 +427,30 @@ class Solution: return True ``` +python3; 简单拼接版本(类似Leetcode131写法): +```python +class Solution: + def restoreIpAddresses(self, s: str) -> List[str]: + global results, path + results = [] + path = [] + self.backtracking(s,0) + return results + + def backtracking(self,s,index): + global results,path + if index == len(s) and len(path)==4: + results.append('.'.join(path)) # 在连接时需要中间间隔符号的话就在''中间写上对应的间隔符 + return + for i in range(index,len(s)): + if len(path)>3: break # 剪枝 + temp = s[index:i+1] + if (int(temp)<256 and int(temp)>0 and temp[0]!='0') or (temp=='0'): + path.append(temp) + self.backtracking(s,i+1) + path.pop() +``` + ## Go ```go diff --git a/problems/0102.二叉树的层序遍历.md b/problems/0102.二叉树的层序遍历.md index 220232a2..36ae1ec6 100644 --- a/problems/0102.二叉树的层序遍历.md +++ b/problems/0102.二叉树的层序遍历.md @@ -23,10 +23,12 @@ * 111.二叉树的最小深度 + ![我要打十个](https://code-thinking.cdn.bcebos.com/gifs/%E6%88%91%E8%A6%81%E6%89%93%E5%8D%81%E4%B8%AA.gif) + # 102.二叉树的层序遍历 [力扣题目链接](https://leetcode.cn/problems/binary-tree-level-order-traversal/) diff --git a/problems/0106.从中序与后序遍历序列构造二叉树.md b/problems/0106.从中序与后序遍历序列构造二叉树.md index f8109f85..c2b2872b 100644 --- a/problems/0106.从中序与后序遍历序列构造二叉树.md +++ b/problems/0106.从中序与后序遍历序列构造二叉树.md @@ -397,6 +397,9 @@ public: }; ``` +## Python + + # 105.从前序与中序遍历序列构造二叉树 [力扣题目链接](https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/) @@ -650,6 +653,37 @@ class Solution { ``` ## Python +```python +class Solution: + def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]: + # 第一步: 特殊情况讨论: 树为空. 或者说是递归终止条件 + if not postorder: + return + + # 第二步: 后序遍历的最后一个就是当前的中间节点 + root_val = postorder[-1] + root = TreeNode(root_val) + + # 第三步: 找切割点. + root_index = inorder.index(root_val) + + # 第四步: 切割inorder数组. 得到inorder数组的左,右半边. + left_inorder = inorder[:root_index] + right_inorder = inorder[root_index + 1:] + + # 第五步: 切割postorder数组. 得到postorder数组的左,右半边. + # ⭐️ 重点1: 中序数组大小一定跟后序数组大小是相同的. + left_postorder = postorder[:len(left_inorder)] + right_postorder = postorder[len(left_inorder): len(postorder) - 1] + + + # 第六步: 递归 + root.left = self.buildTree(left_inorder, left_postorder) + root.right = self.buildTree(right_inorder, right_postorder) + + # 第七步: 返回答案 + return root +``` 105.从前序与中序遍历序列构造二叉树 diff --git a/problems/0112.路径总和.md b/problems/0112.路径总和.md index 7aa5f2a1..b1f01336 100644 --- a/problems/0112.路径总和.md +++ b/problems/0112.路径总和.md @@ -155,14 +155,14 @@ public: 以上代码精简之后如下: ```cpp -class solution { +class Solution { public: bool hasPathSum(TreeNode* root, int sum) { - if (root == null) return false; + if (!root) return false; if (!root->left && !root->right && sum == root->val) { return true; } - return haspathsum(root->left, sum - root->val) || haspathsum(root->right, sum - root->val); + return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val); } }; ``` diff --git a/problems/0130.被围绕的区域.md b/problems/0130.被围绕的区域.md index 528d2042..c2aa5696 100644 --- a/problems/0130.被围绕的区域.md +++ b/problems/0130.被围绕的区域.md @@ -83,6 +83,7 @@ public: ``` ## 其他语言版本 +

diff --git a/problems/0134.加油站.md b/problems/0134.加油站.md index ade84773..192e58a6 100644 --- a/problems/0134.加油站.md +++ b/problems/0134.加油站.md @@ -88,7 +88,7 @@ public: * 情况一:如果gas的总和小于cost总和,那么无论从哪里出发,一定是跑不了一圈的 * 情况二:rest[i] = gas[i]-cost[i]为一天剩下的油,i从0开始计算累加到最后一站,如果累加没有出现负数,说明从0出发,油就没有断过,那么0就是起点。 -* 情况三:如果累加的最小值是负数,汽车就要从非0节点出发,从后向前,看哪个节点能这个负数填平,能把这个负数填平的节点就是出发节点。 +* 情况三:如果累加的最小值是负数,汽车就要从非0节点出发,从后向前,看哪个节点能把这个负数填平,能把这个负数填平的节点就是出发节点。 C++代码如下: diff --git a/problems/0151.翻转字符串里的单词.md b/problems/0151.翻转字符串里的单词.md index a12d8f76..dc12ae23 100644 --- a/problems/0151.翻转字符串里的单词.md +++ b/problems/0151.翻转字符串里的单词.md @@ -442,7 +442,7 @@ class Solution: while left <= right and s[left] == ' ': #去除开头的空格 left += 1 while left <= right and s[right] == ' ': #去除结尾的空格 - right = right-1 + right -= 1 tmp = [] while left <= right: #去除单词中间多余的空格 if s[left] != ' ': diff --git a/problems/0206.翻转链表.md b/problems/0206.翻转链表.md index 44146bb4..7db80fe1 100644 --- a/problems/0206.翻转链表.md +++ b/problems/0206.翻转链表.md @@ -628,6 +628,43 @@ impl Solution { } } ``` +C#: +三指针法, 感觉会更直观: + +```cs +public LinkNumbers Reverse() +{ + ///用三指针,写的过程中能够弥补二指针在翻转过程中的想象 + LinkNumbers pre = null; + var move = root; + var next = root; + + while (next != null) + { + next = next.linknext; + move.linknext = pre; + pre = move; + move = next; + } + root = pre; + return root; +} + +///LinkNumbers的定义 +public class LinkNumbers +{ + ///

+ /// 链表值 + /// + public int value { get; set; } + + /// + /// 链表指针 + /// + public LinkNumbers linknext { get; set; } +} +``` +

diff --git a/problems/0232.用栈实现队列.md b/problems/0232.用栈实现队列.md index 4983c93a..efeb1046 100644 --- a/problems/0232.用栈实现队列.md +++ b/problems/0232.用栈实现队列.md @@ -38,7 +38,7 @@ queue.empty(); // 返回 false ## 思路 -《代码随想录》算法公开课:[栈的基本操作! | LeetCode:232.用栈实现队列](https://www.bilibili.com/video/BV1nY4y1w7VC),相信结合视频再看本篇题解,更有助于大家对链表的理解。 +《代码随想录》算法公开课:[栈的基本操作! | LeetCode:232.用栈实现队列](https://www.bilibili.com/video/BV1nY4y1w7VC),相信结合视频再看本篇题解,更有助于大家对栈和队列的理解。 这是一道模拟题,不涉及到具体算法,考察的就是对栈和队列的掌握程度。 @@ -662,3 +662,4 @@ impl MyQueue { + diff --git a/problems/0257.二叉树的所有路径.md b/problems/0257.二叉树的所有路径.md index 2d796671..d0c190a0 100644 --- a/problems/0257.二叉树的所有路径.md +++ b/problems/0257.二叉树的所有路径.md @@ -468,7 +468,7 @@ class Solution { --- ## Python: 递归法+隐形回溯 -```Python3 +```Python # Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): @@ -499,7 +499,7 @@ class Solution: 迭代法: -```python3 +```Python from collections import deque diff --git a/problems/0349.两个数组的交集.md b/problems/0349.两个数组的交集.md index f8f41097..ed6a5d97 100644 --- a/problems/0349.两个数组的交集.md +++ b/problems/0349.两个数组的交集.md @@ -137,8 +137,19 @@ class Solution { resSet.add(i); } } - //将结果集合转为数组 + + //方法1:将结果集合转为数组 + return resSet.stream().mapToInt(x -> x).toArray(); + + //方法2:另外申请一个数组存放setRes中的元素,最后返回数组 + int[] arr = new int[setRes.size()]; + int j = 0; + for(int i : setRes){ + arr[j++] = i; + } + + return arr; } } ``` @@ -423,3 +434,4 @@ C#: + diff --git a/problems/0455.分发饼干.md b/problems/0455.分发饼干.md index eec471af..2471cb68 100644 --- a/problems/0455.分发饼干.md +++ b/problems/0455.分发饼干.md @@ -52,6 +52,7 @@ C++代码整体如下: ```CPP +// 版本一 // 时间复杂度:O(nlogn) // 空间复杂度:O(1) class Solution { @@ -61,8 +62,8 @@ public: sort(s.begin(), s.end()); int index = s.size() - 1; // 饼干数组的下标 int result = 0; - for (int i = g.size() - 1; i >= 0; i--) { - if (index >= 0 && s[index] >= g[i]) { + for (int i = g.size() - 1; i >= 0; i--) { // 遍历胃口 + if (index >= 0 && s[index] >= g[i]) { // 遍历饼干 result++; index--; } @@ -76,6 +77,26 @@ public: 有的同学看到要遍历两个数组,就想到用两个for循环,那样逻辑其实就复杂了。 + +### 注意事项 + +注意版本一的代码中,可以看出来,是先遍历的胃口,在遍历的饼干,那么可不可以 先遍历 饼干,在遍历胃口呢? + +其实是不可以的。 + +外面的for 是里的下标i 是固定移动的,而if里面的下标 index 是符合条件才移动的。 + +如果 for 控制的是饼干, if 控制胃口,就是出现如下情况 : + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20230112102848.png) + +if 里的 index 指向 胃口 10, for里的i指向饼干9,因为 饼干9 满足不了 胃口10,所以 i 持续向前移动,而index 走不到` s[index] >= g[i]` 的逻辑,所以index不会移动,那么当i 持续向前移动,最后所有的饼干都匹配不上。 + +所以 一定要for 控制 胃口,里面的if控制饼干。 + + +### 其他思路 + **也可以换一个思路,小饼干先喂饱小胃口** 代码如下: @@ -87,15 +108,19 @@ public: sort(g.begin(),g.end()); sort(s.begin(),s.end()); int index = 0; - for(int i = 0;i < s.size();++i){ - if(index < g.size() && g[index] <= s[i]){ + for(int i = 0; i < s.size(); i++) { // 饼干 + if(index < g.size() && g[index] <= s[i]){ // 胃口 index++; } } return index; } }; -``` +``` + +细心的录友可以发现,这种写法,两个循环的顺序改变了,先遍历的饼干,在遍历的胃口,这是因为遍历顺序变了,我们是从小到大遍历。 + +理由在上面 “注意事项”中 已经讲过。 ## 总结 @@ -148,7 +173,7 @@ class Solution { ### Python ```python class Solution: - # 思路1:优先考虑胃饼干 + # 思路1:优先考虑小胃口 def findContentChildren(self, g: List[int], s: List[int]) -> int: g.sort() s.sort() @@ -160,7 +185,7 @@ class Solution: ``` ```python class Solution: - # 思路2:优先考虑胃口 + # 思路2:优先考虑大胃口 def findContentChildren(self, g: List[int], s: List[int]) -> int: g.sort() s.sort() diff --git a/problems/0538.把二叉搜索树转换为累加树.md b/problems/0538.把二叉搜索树转换为累加树.md index 3cb9d3db..8d4b97f1 100644 --- a/problems/0538.把二叉搜索树转换为累加树.md +++ b/problems/0538.把二叉搜索树转换为累加树.md @@ -209,29 +209,28 @@ class Solution { # self.right = right class Solution: def __init__(self): - self.pre = TreeNode() + self.count = 0 def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + if root == None: + return ''' 倒序累加替换: - [2, 5, 13] -> [[2]+[1]+[0], [2]+[1], [2]] -> [20, 18, 13] ''' - self.traversal(root) - return root - - def traversal(self, root: TreeNode) -> None: - # 因为要遍历整棵树,所以递归函数不需要返回值 - # Base Case - if not root: - return None - # 单层递归逻辑:中序遍历的反译 - 右中左 - self.traversal(root.right) # 右 + # 右 + self.convertBST(root.right) + # 中 # 中节点:用当前root的值加上pre的值 - root.val += self.pre.val # 中 - self.pre = root + self.count += root.val - self.traversal(root.left) # 左 + root.val = self.count + + # 左 + self.convertBST(root.left) + + return root + ``` ## Go diff --git a/problems/1005.K次取反后最大化的数组和.md b/problems/1005.K次取反后最大化的数组和.md index 08848a01..cdf42511 100644 --- a/problems/1005.K次取反后最大化的数组和.md +++ b/problems/1005.K次取反后最大化的数组和.md @@ -44,7 +44,7 @@ 那么如果将负数都转变为正数了,K依然大于0,此时的问题是一个有序正整数序列,如何转变K次正负,让 数组和 达到最大。 -那么又是一个贪心:局部最优:只找数值最小的正整数进行反转,当前数值可以达到最大(例如正整数数组{5, 3, 1},反转1 得到-1 比 反转5得到的-5 大多了),全局最优:整个 数组和 达到最大。 +那么又是一个贪心:局部最优:只找数值最小的正整数进行反转,当前数值和可以达到最大(例如正整数数组{5, 3, 1},反转1 得到-1 比 反转5得到的-5 大多了),全局最优:整个 数组和 达到最大。 虽然这道题目大家做的时候,可能都不会去想什么贪心算法,一鼓作气,就AC了。 diff --git a/problems/1020.飞地的数量.md b/problems/1020.飞地的数量.md index b7cba7a6..3da37376 100644 --- a/problems/1020.飞地的数量.md +++ b/problems/1020.飞地的数量.md @@ -144,6 +144,11 @@ public: } }; ``` +## 类似题目 + +* 1254. 统计封闭岛屿的数目 + +

diff --git a/problems/前序/ACM模式如何构建二叉树.md b/problems/前序/ACM模式如何构建二叉树.md index 01d5b255..42bf7af0 100644 --- a/problems/前序/ACM模式如何构建二叉树.md +++ b/problems/前序/ACM模式如何构建二叉树.md @@ -280,7 +280,7 @@ public class Solution { ## Python -```Python3 +```Python class TreeNode: def __init__(self, val = 0, left = None, right = None): self.val = val diff --git a/problems/前序/vim.md b/problems/前序/vim.md index f84e70ac..5c5910d2 100644 --- a/problems/前序/vim.md +++ b/problems/前序/vim.md @@ -66,7 +66,7 @@ IDE那么很吃内存,打开个IDE卡半天,用VIM就很轻便了,秒开 ## 安装 -PowerVim的安防非常简单,我已经写好了安装脚本,只要执行以下就可以安装,而且不会影响你之前的vim配置,之前的配置都给做了备份,大家看一下脚本就知道备份在哪里了。 +PowerVim的安装非常简单,我已经写好了安装脚本,只要执行以下就可以安装,而且不会影响你之前的vim配置,之前的配置都给做了备份,大家看一下脚本就知道备份在哪里了。 安装过程非常简单: ```bash diff --git a/problems/周总结/20201003二叉树周末总结.md b/problems/周总结/20201003二叉树周末总结.md index 18bbf37f..5f59b040 100644 --- a/problems/周总结/20201003二叉树周末总结.md +++ b/problems/周总结/20201003二叉树周末总结.md @@ -2,7 +2,7 @@ 本周赶上了十一国庆,估计大家已经对本周末没什么概念了,但是我们该做总结还是要做总结的。 -本周的主题其实是**简单但并不简单**,本周所选的题目大多是看一下就会的题目,但是大家看完本周的文章估计也发现了,二叉树的简答题目其实里面都藏了很多细节。 这些细节我都给大家展现了出来。 +本周的主题其实是**简单但并不简单**,本周所选的题目大多是看一下就会的题目,但是大家看完本周的文章估计也发现了,二叉树的简单题目其实里面都藏了很多细节。 这些细节我都给大家展现了出来。 ## 周一