diff --git a/README.md b/README.md index c006fd6b..022a45e8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## 一些闲话: +👉 推荐 [在线阅读](http://programmercarl.com/) (Github在国内访问经常不稳定) > 1. **介绍**:本项目是一套完整的刷题计划,旨在帮助大家少走弯路,循序渐进学算法,[关注作者](#关于作者) > 2. **PDF版本** : [「代码随想录」算法精讲 PDF 版本](https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ) 。 @@ -119,30 +119,40 @@ * [递归算法的时间与空间复杂度分析!](./problems/前序/递归算法的时间与空间复杂度分析.md) * [刷了这么多题,你了解自己代码的内存消耗么?](./problems/前序/刷了这么多题,你了解自己代码的内存消耗么?.md) -(持续更新中.....) + +## 社群 + +* [准备秋招的录友们,组织在这里!](https://mp.weixin.qq.com/s/xX4LFwZQIG_XiQAxVBrfeA) +* [准备社招的录友们,组织在这里!](https://mp.weixin.qq.com/s/mbQ3s17ZJ4LXFRb-VD58Ww) ## 知识星球精选 -1. [选择方向的时候,我也迷茫了](https://mp.weixin.qq.com/s/ZCzFiAHZHLqHPLJQXNm75g) -2. [刷题就用库函数了,怎么了?](https://mp.weixin.qq.com/s/6K3_OSaudnHGq2Ey8vqYfg) -3. [关于实习,大家可能有点迷茫!](https://mp.weixin.qq.com/s/xcxzi7c78kQGjvZ8hh7taA) -4. [马上秋招了,慌得很!](https://mp.weixin.qq.com/s/7q7W8Cb2-a5U5atZdOnOFA) -5. [Carl看了上百份简历,总结了这些!](https://mp.weixin.qq.com/s/sJa87MZD28piCOVMFkIbwQ) -6. [面试中遇到了发散性问题.....](https://mp.weixin.qq.com/s/SSonDxi2pjkSVwHNzZswng) -7. [英语到底重不重要!](https://mp.weixin.qq.com/s/1PRZiyF_-TVA-ipwDNjdKw) -8. [计算机专业要不要读研!](https://mp.weixin.qq.com/s/c9v1L3IjqiXtkNH7sOMAdg) -9. [秋招和提前批都越来越提前了....](https://mp.weixin.qq.com/s/SNFiRDx8CKyjhTPlys6ywQ) -10. [你的简历里「专业技能」写的够专业么?](https://mp.weixin.qq.com/s/bp6y-e5FVN28H9qc8J9zrg) -11. [对于秋招,实习生也有烦恼....](https://mp.weixin.qq.com/s/ka07IPryFnfmIjByFFcXDg) -12. [华为提前批已经开始了.....](https://mp.weixin.qq.com/s/OC35QDG8pn5OwLpCxieStw) -13. [大厂新人培养体系应该是什么样的?](https://mp.weixin.qq.com/s/WBaPCosOljB5NEkFL2GhOQ) +* [HR面注意事项](https://mp.weixin.qq.com/s/0mDyCyCBfa0DeGov3Pebnw) +* [刷题攻略要刷两遍!](https://mp.weixin.qq.com/s/e3_L7FZglY4UlTVvKolZyQ) +* [秋招进行中的迷茫与焦虑......](https://mp.weixin.qq.com/s/X15MUw4sfH_AQNHdAivEvg) +* [大厂新人培养体系应该是什么样的?](https://mp.weixin.qq.com/s/WBaPCosOljB5NEkFL2GhOQ) +* [你的简历里「专业技能」写的够专业么?](https://mp.weixin.qq.com/s/bp6y-e5FVN28H9qc8J9zrg) +* [Carl看了上百份简历,总结了这些!](https://mp.weixin.qq.com/s/sJa87MZD28piCOVMFkIbwQ) +* [备战2022届秋招](https://mp.weixin.qq.com/s/7q7W8Cb2-a5U5atZdOnOFA) +* [技术不太好,如果选择方向](https://mp.weixin.qq.com/s/ZCzFiAHZHLqHPLJQXNm75g) +* [刷题要不要使用库函数](https://mp.weixin.qq.com/s/6K3_OSaudnHGq2Ey8vqYfg) +* [关于实习的几点问题](https://mp.weixin.qq.com/s/xcxzi7c78kQGjvZ8hh7taA) +* [面试中遇到了发散性问题,怎么帮?](https://mp.weixin.qq.com/s/SSonDxi2pjkSVwHNzZswng) +* [英语到底重不重要!](https://mp.weixin.qq.com/s/1PRZiyF_-TVA-ipwDNjdKw) +* [计算机专业要不要读研!](https://mp.weixin.qq.com/s/c9v1L3IjqiXtkNH7sOMAdg) +* [关于提前批的一些建议](https://mp.weixin.qq.com/s/SNFiRDx8CKyjhTPlys6ywQ) +* [已经在实习的录友要如何准备秋招](https://mp.weixin.qq.com/s/ka07IPryFnfmIjByFFcXDg) +* [华为提前批已经开始了](https://mp.weixin.qq.com/s/OC35QDG8pn5OwLpCxieStw) ## 杂谈 +* [「代码随想录」刷题网站上线](https://mp.weixin.qq.com/s/-6rd_g7LrVD1fuKBYk2tXQ)。 * [LeetCode-Master上榜了](https://mp.weixin.qq.com/s/wZRTrA9Rbvgq1yEkSw4vfQ) +* [上榜之后,都有哪些变化?](https://mp.weixin.qq.com/s/VJBV0qSBthjnbbmW-lctLA) * [大半年过去了......](https://mp.weixin.qq.com/s/lubfeistPxBLSQIe5XYg5g) * [一万录友在B站学算法!](https://mp.weixin.qq.com/s/Vzq4zkMZY7erKeu0fqGLgw) + ## 数组 1. [数组过于简单,但你该了解这些!](./problems/数组理论基础.md) @@ -241,7 +251,7 @@ 15. [二叉树:以为使用了递归,其实还隐藏着回溯](./problems/二叉树中递归带着回溯.md) 16. [二叉树:做了这么多题目了,我的左叶子之和是多少?](./problems/0404.左叶子之和.md) 17. [二叉树:我的左下角的值是多少?](./problems/0513.找树左下角的值.md) -18. [二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?](./problems/0112.路径总和.md) +18. [二叉树:路径总和](./problems/0112.路径总和.md) 19. [二叉树:构造二叉树登场!](./problems/0106.从中序与后序遍历序列构造二叉树.md) 20. [二叉树:构造一棵最大的二叉树](./problems/0654.最大二叉树.md) 21. [本周小结!(二叉树系列三)](./problems/周总结/20201010二叉树周末总结.md) @@ -409,6 +419,7 @@ 2. [单调栈:下一个更大元素I](./problems/0496.下一个更大元素I.md) 3. [单调栈:下一个更大元素II](./problems/0503.下一个更大元素II.md) 4. [单调栈:接雨水](./problems/0042.接雨水.md) +5. [单调栈:柱状图中最大的矩形](./problems/0084.柱状图中最大的矩形.md) (持续更新中....) @@ -451,7 +462,6 @@ * [24.两两交换链表中的节点](./problems/0024.两两交换链表中的节点.md) * [234.回文链表](./problems/0234.回文链表.md) * [143.重排链表](./problems/0143.重排链表.md)【数组】【双向队列】【直接操作链表】 -* [234.回文链表](./problems/0234.回文链表.md) * [141.环形链表](./problems/0141.环形链表.md) ## 哈希表 diff --git a/problems/0001.两数之和.md b/problems/0001.两数之和.md index 5be94996..fd17af62 100644 --- a/problems/0001.两数之和.md +++ b/problems/0001.两数之和.md @@ -9,7 +9,7 @@ ## 1. 两数之和 -https://leetcode-cn.com/problems/two-sum/ +[力扣题目链接](https://leetcode-cn.com/problems/two-sum/) 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 @@ -29,10 +29,10 @@ https://leetcode-cn.com/problems/two-sum/ 很明显暴力的解法是两层for循环查找,时间复杂度是O(n^2)。 建议大家做这道题目之前,先做一下这两道 -* [242. 有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA) -* [349. 两个数组的交集](https://mp.weixin.qq.com/s/aMSA5zrp3jJcLjuSB0Es2Q) +* [242. 有效的字母异位词](https://www.programmercarl.com/0242.有效的字母异位词.html) +* [349. 两个数组的交集](https://www.programmercarl.com/0349.两个数组的交集.html) -[242. 有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA) 这道题目是用数组作为哈希表来解决哈希问题,[349. 两个数组的交集](https://mp.weixin.qq.com/s/aMSA5zrp3jJcLjuSB0Es2Q)这道题目是通过set作为哈希表来解决哈希问题。 +[242. 有效的字母异位词](https://www.programmercarl.com/0242.有效的字母异位词.html) 这道题目是用数组作为哈希表来解决哈希问题,[349. 两个数组的交集](https://www.programmercarl.com/0349.两个数组的交集.html)这道题目是通过set作为哈希表来解决哈希问题。 本题呢,则要使用map,那么来看一下使用数组和set来做哈希法的局限。 @@ -51,7 +51,7 @@ C++中map,有三种类型: std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。 -同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。 更多哈希表的理论知识请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)。 +同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。 更多哈希表的理论知识请看[关于哈希表,你该了解这些!](https://www.programmercarl.com/哈希表理论基础.html)。 **这道题目中并不需要key有序,选择std::unordered_map 效率更高!** @@ -62,7 +62,7 @@ std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底 C++代码: -```C++ +```CPP class Solution { public: vector twoSum(vector& nums, int target) { @@ -110,13 +110,14 @@ Python: ```python class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: - hashmap={} - for ind,num in enumerate(nums): - hashmap[num] = ind - for i,num in enumerate(nums): - j = hashmap.get(target - num) - if j is not None and i!=j: - return [i,j] + records = dict() + + # 用枚举更方便,就不需要通过索引再去取当前位置的值 + for idx, val in enumerate(nums): + if target - val not in records: + records[val] = idx + else: + return [records[target - val], idx] # 如果存在就返回字典记录索引和当前索引 ``` @@ -210,4 +211,4 @@ function twoSum(array $nums, int $target): array * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0005.最长回文子串.md b/problems/0005.最长回文子串.md index 0063b358..de20feeb 100644 --- a/problems/0005.最长回文子串.md +++ b/problems/0005.最长回文子串.md @@ -67,7 +67,7 @@ 以上三种情况分析完了,那么递归公式如下: -```C++ +```CPP if (s[i] == s[j]) { if (j - i <= 1) { // 情况一 和 情况二 dp[i][j] = true; @@ -81,7 +81,7 @@ if (s[i] == s[j]) { 在得到[i,j]区间是否是回文子串的时候,直接保存最长回文子串的左边界和右边界,代码如下: -```C++ +```CPP if (s[i] == s[j]) { if (j - i <= 1) { // 情况一 和 情况二 dp[i][j] = true; @@ -120,7 +120,7 @@ dp[i + 1][j - 1] 在 dp[i][j]的左下角,如图: 代码如下: -```C++ +```CPP for (int i = s.size() - 1; i >= 0; i--) { // 注意遍历顺序 for (int j = i; j < s.size(); j++) { if (s[i] == s[j]) { @@ -150,7 +150,7 @@ for (int i = s.size() - 1; i >= 0; i--) { // 注意遍历顺序 以上分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: string longestPalindrome(string s) { @@ -181,7 +181,7 @@ public: ``` 以上代码是为了凸显情况一二三,当然是可以简洁一下的,如下: -```C++ +```CPP class Solution { public: string longestPalindrome(string s) { @@ -226,7 +226,7 @@ public: **这两种情况可以放在一起计算,但分别计算思路更清晰,我倾向于分别计算**,代码如下: -```C++ +```CPP class Solution { public: int left = 0; @@ -270,6 +270,23 @@ public: ## Python ```python +class Solution: + def longestPalindrome(self, s: str) -> str: + dp = [[False] * len(s) for _ in range(len(s))] + maxlenth = 0 + left = 0 + right = 0 + for i in range(len(s) - 1, -1, -1): + for j in range(i, len(s)): + if s[j] == s[i]: + if j - i <= 1 or dp[i + 1][j - 1]: + dp[i][j] = True + if dp[i][j] and j - i + 1 > maxlenth: + maxlenth = j - i + 1 + left = i + right = j + return s[left:right + 1] + ``` ## Go @@ -286,6 +303,6 @@ public: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0015.三数之和.md b/problems/0015.三数之和.md index 36abb58c..9a3a8d5b 100644 --- a/problems/0015.三数之和.md +++ b/problems/0015.三数之和.md @@ -46,7 +46,7 @@ https://leetcode-cn.com/problems/3sum/ 大家可以尝试使用哈希法写一写,就知道其困难的程度了。 哈希法C++代码: -```C++ +```CPP class Solution { public: vector> threeSum(vector& nums) { @@ -107,7 +107,7 @@ public: C++代码代码如下: -```C++ +```CPP class Solution { public: vector> threeSum(vector& nums) { @@ -399,4 +399,4 @@ function threeSum(array $nums): array * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0017.电话号码的字母组合.md b/problems/0017.电话号码的字母组合.md index 1562052c..7aa72e1b 100644 --- a/problems/0017.电话号码的字母组合.md +++ b/problems/0017.电话号码的字母组合.md @@ -416,4 +416,4 @@ var letterCombinations = function(digits) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0018.四数之和.md b/problems/0018.四数之和.md index 0caf12be..75cd9e8e 100644 --- a/problems/0018.四数之和.md +++ b/problems/0018.四数之和.md @@ -67,7 +67,7 @@ https://leetcode-cn.com/problems/4sum/ C++代码 -```C++ +```CPP class Solution { public: vector> fourSum(vector& nums, int target) { @@ -201,6 +201,54 @@ class Solution(object): ``` Go: +```go +func fourSum(nums []int, target int) [][]int { + if len(nums) < 4 { + return nil + } + sort.Ints(nums) + var res [][]int + for i := 0; i < len(nums)-3; i++ { + n1 := nums[i] + // if n1 > target { // 不能这样写,因为可能是负数 + // break + // } + if i > 0 && n1 == nums[i-1] { + continue + } + for j := i + 1; j < len(nums)-2; j++ { + n2 := nums[j] + if j > i+1 && n2 == nums[j-1] { + continue + } + l := j + 1 + r := len(nums) - 1 + for l < r { + n3 := nums[l] + n4 := nums[r] + sum := n1 + n2 + n3 + n4 + if sum < target { + l++ + } else if sum > target { + r-- + } else { + res = append(res, []int{n1, n2, n3, n4}) + for l < r && n3 == nums[l+1] { // 去重 + l++ + } + for l < r && n4 == nums[r-1] { // 去重 + r-- + } + // 找到答案时,双指针同时靠近 + r-- + l++ + } + } + } + } + return res +} +``` javaScript: @@ -241,4 +289,4 @@ var fourSum = function(nums, target) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0019.删除链表的倒数第N个节点.md b/problems/0019.删除链表的倒数第N个节点.md index 52735794..3e1a682c 100644 --- a/problems/0019.删除链表的倒数第N个节点.md +++ b/problems/0019.删除链表的倒数第N个节点.md @@ -58,7 +58,7 @@ 此时不难写出如下C++代码: -```C++ +```CPP class Solution { public: ListNode* removeNthFromEnd(ListNode* head, int n) { @@ -184,9 +184,28 @@ var removeNthFromEnd = function(head, n) { return ret.next; }; ``` +Kotlin: +```Kotlin +fun removeNthFromEnd(head: ListNode?, n: Int): ListNode? { + val pre = ListNode(0).apply { + this.next = head + } + var fastNode: ListNode? = pre + var slowNode: ListNode? = pre + for (i in 0..n) { + fastNode = fastNode?.next + } + while (fastNode != null) { + slowNode = slowNode?.next + fastNode = fastNode.next + } + slowNode?.next = slowNode?.next?.next + return pre.next +} +``` ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0020.有效的括号.md b/problems/0020.有效的括号.md index 178bc6f8..a26aa308 100644 --- a/problems/0020.有效的括号.md +++ b/problems/0020.有效的括号.md @@ -108,7 +108,7 @@ cd a/b/c/../../ 实现C++代码如下: -```C++ +```CPP class Solution { public: bool isValid(string s) { @@ -264,4 +264,4 @@ var isValid = function(s) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0024.两两交换链表中的节点.md b/problems/0024.两两交换链表中的节点.md index 66e149e6..91e566dd 100644 --- a/problems/0024.两两交换链表中的节点.md +++ b/problems/0024.两两交换链表中的节点.md @@ -43,7 +43,7 @@ https://leetcode-cn.com/problems/swap-nodes-in-pairs/ 对应的C++代码实现如下: (注释中详细和如上图中的三步做对应) -```C++ +```CPP class Solution { public: ListNode* swapPairs(ListNode* head) { @@ -86,6 +86,34 @@ public: ## 其他语言版本 +C: +``` +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * struct ListNode *next; + * }; + */ + + +struct ListNode* swapPairs(struct ListNode* head){ + //使用双指针避免使用中间变量 + typedef struct ListNode ListNode; + ListNode *fakehead = (ListNode *)malloc(sizeof(ListNode)); + fakehead->next = head; + ListNode* right = fakehead->next; + ListNode* left = fakehead; + while(left && right && right->next ){ + left->next = right->next; + right->next = left->next->next; + left->next->next = right; + left = right; + right = left->next; + } + return fakehead->next; +} +``` Java: @@ -200,9 +228,30 @@ var swapPairs = function (head) { }; ``` +Kotlin: + +```kotlin +fun swapPairs(head: ListNode?): ListNode? { + val dummyNode = ListNode(0).apply { + this.next = head + } + var cur: ListNode? = dummyNode + while (cur?.next != null && cur.next?.next != null) { + val temp = cur.next + val temp2 = cur.next?.next?.next + cur.next = cur.next?.next + cur.next?.next = temp + cur.next?.next?.next = temp2 + cur = cur.next?.next + } + return dummyNode.next +} +``` + + ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0027.移除元素.md b/problems/0027.移除元素.md index f1187db7..3819d6a5 100644 --- a/problems/0027.移除元素.md +++ b/problems/0027.移除元素.md @@ -48,7 +48,7 @@ 代码如下: -```C++ +```CPP // 时间复杂度:O(n^2) // 空间复杂度:O(1) class Solution { @@ -85,7 +85,7 @@ public: 后序都会一一介绍到,本题代码如下: -```C++ +```CPP // 时间复杂度:O(n) // 空间复杂度:O(1) class Solution { @@ -216,8 +216,27 @@ fn main() { println!("{:?}",remove_element(&mut nums, 5)); } ``` + +Swift: + +```swift +func removeElement(_ nums: inout [Int], _ val: Int) -> Int { + var slowIndex = 0 + + for fastIndex in 0.. +
diff --git a/problems/0028.实现strStr.md b/problems/0028.实现strStr.md index 69f8c9d6..4fb418f0 100644 --- a/problems/0028.实现strStr.md +++ b/problems/0028.实现strStr.md @@ -315,7 +315,7 @@ next[i] = j; 最后整体构建next数组的函数代码如下: -```C++ +```CPP void getNext(int* next, const string& s){     int j = -1;     next[0] = j; @@ -386,7 +386,7 @@ if (j == (t.size() - 1) ) { 那么使用next数组,用模式串匹配文本串的整体代码如下: -```C++ +```CPP int j = -1; // 因为next数组里记录的起始位置为-1 for (int i = 0; i < s.size(); i++) { // 注意i就从0开始     while(j >= 0 && s[i] != t[j + 1]) { // 不匹配 @@ -405,7 +405,7 @@ for (int i = 0; i < s.size(); i++) { // 注意i就从0开始 # 前缀表统一减一 C++代码实现 -```C++ +```CPP class Solution { public:     void getNext(int* next, const string& s) { @@ -457,7 +457,7 @@ public: 我给出的getNext的实现为:(前缀表统一减一) -```C++ +```CPP void getNext(int* next, const string& s) {     int j = -1;     next[0] = j; @@ -479,7 +479,7 @@ void getNext(int* next, const string& s) { 那么前缀表不减一来构建next数组,代码如下: -```C++ +```CPP void getNext(int* next, const string& s) { int j = 0; next[0] = 0; @@ -502,7 +502,7 @@ void getNext(int* next, const string& s) { 实现代码如下: -```C++ +```CPP class Solution { public: void getNext(int* next, const string& s) { @@ -808,7 +808,93 @@ func strStr(haystack string, needle string) int { } ``` +JavaScript版本 +> 前缀表统一减一 + +```javascript +/** + * @param {string} haystack + * @param {string} needle + * @return {number} + */ +var strStr = function (haystack, needle) { + if (needle.length === 0) + return 0; + + const getNext = (needle) => { + let next = []; + let j = -1; + next.push(j); + + for (let i = 1; i < needle.length; ++i) { + while (j >= 0 && needle[i] !== needle[j + 1]) + j = next[j]; + if (needle[i] === needle[j + 1]) + j++; + next.push(j); + } + + return next; + } + + let next = getNext(needle); + let j = -1; + for (let i = 0; i < haystack.length; ++i) { + while (j >= 0 && haystack[i] !== needle[j + 1]) + j = next[j]; + if (haystack[i] === needle[j + 1]) + j++; + if (j === needle.length - 1) + return (i - needle.length + 1); + } + + return -1; +}; +``` + +> 前缀表统一不减一 + +```javascript +/** + * @param {string} haystack + * @param {string} needle + * @return {number} + */ +var strStr = function (haystack, needle) { + if (needle.length === 0) + return 0; + + const getNext = (needle) => { + let next = []; + let j = 0; + next.push(j); + + for (let i = 1; i < needle.length; ++i) { + while (j > 0 && needle[i] !== needle[j]) + j = next[j - 1]; + if (needle[i] === needle[j]) + j++; + next.push(j); + } + + return next; + } + + let next = getNext(needle); + let j = 0; + for (let i = 0; i < haystack.length; ++i) { + while (j > 0 && haystack[i] !== needle[j]) + j = next[j - 1]; + if (haystack[i] === needle[j]) + j++; + if (j === needle.length) + return (i - needle.length + 1); + } + + return -1; +}; +``` @@ -816,4 +902,4 @@ func strStr(haystack string, needle string) int { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0031.下一个排列.md b/problems/0031.下一个排列.md index fbe31eb3..10ac9df4 100644 --- a/problems/0031.下一个排列.md +++ b/problems/0031.下一个排列.md @@ -75,7 +75,7 @@ 对应的C++代码如下: -```C++ +```CPP class Solution { public: void nextPermutation(vector& nums) { @@ -99,6 +99,24 @@ public: ## Java ```java +class Solution { + public void nextPermutation(int[] nums) { + for (int i = nums.length - 1; i >= 0; i--) { + for (int j = nums.length - 1; j > i; j--) { + if (nums[j] > nums[i]) { + // 交换 + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + // [i + 1, nums.length) 内元素升序排序 + Arrays.sort(nums, i + 1, nums.length); + return; + } + } + } + Arrays.sort(nums); // 不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。 + } +} ``` ## Python @@ -120,5 +138,5 @@ public: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md b/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md index 04f5eaf7..f83de1a8 100644 --- a/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md +++ b/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md @@ -14,7 +14,7 @@ 如果数组中不存在目标值 target,返回 [-1, -1]。 进阶:你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗? -  + 示例 1: * 输入:nums = [5,7,7,8,8,10], target = 8 @@ -50,21 +50,21 @@ 接下来,在去寻找左边界,和右边界了。 -采用二分法来取寻找左右边界,为了让代码清晰,我分别写两个二分来寻找左边界和右边界。 +采用二分法来去寻找左右边界,为了让代码清晰,我分别写两个二分来寻找左边界和右边界。 **刚刚接触二分搜索的同学不建议上来就像如果用一个二分来查找左右边界,很容易把自己绕进去,建议扎扎实实的写两个二分分别找左边界和右边界** ## 寻找右边界 -先来寻找右边界,至于二分查找,如果看过[为什么每次遇到二分法,都是一看就会,一写就废](https://mp.weixin.qq.com/s/fCf5QbPDtE6SSlZ1yh_q8Q)就会知道,二分查找中什么时候用while (left <= right),有什么时候用while (left < right),其实只要清楚**循环不变量**,很容易区分两种写法。 +先来寻找右边界,至于二分查找,如果看过[704.二分查找](https://mp.weixin.qq.com/s/4X-8VRgnYRGd5LYGZ33m4w)就会知道,二分查找中什么时候用while (left <= right),有什么时候用while (left < right),其实只要清楚**循环不变量**,很容易区分两种写法。 -那么这里我采用while (left <= right)的写法,区间定义为[left, right],即左闭又闭的区间(如果这里有点看不懂了,强烈建议把[为什么每次遇到二分法,都是一看就会,一写就废](https://mp.weixin.qq.com/s/fCf5QbPDtE6SSlZ1yh_q8Q)这篇文章先看了,在把「leetcode:35.搜索插入位置」做了之后在做这道题目就好很多了) +那么这里我采用while (left <= right)的写法,区间定义为[left, right],即左闭又闭的区间(如果这里有点看不懂了,强烈建议把[704.二分查找](https://mp.weixin.qq.com/s/4X-8VRgnYRGd5LYGZ33m4w)这篇文章先看了,704题目做了之后再做这道题目就好很多了) 确定好:计算出来的右边界是不包好target的右边界,左边界同理。 可以写出如下代码 -```C++ +```CPP // 二分查找,寻找target的右边界(不包括target) // 如果rightBorder为没有被赋值(即target在数组范围的左边,例如数组[3,3],target为2),为了处理情况一 int getRightBorder(vector& nums, int target) { @@ -86,7 +86,7 @@ int getRightBorder(vector& nums, int target) { ## 寻找左边界 -```C++ +```CPP // 二分查找,寻找target的左边界leftBorder(不包括target) // 如果leftBorder没有被赋值(即target在数组范围的右边,例如数组[3,3],target为4),为了处理情况一 int getLeftBorder(vector& nums, int target) { @@ -110,7 +110,7 @@ int getLeftBorder(vector& nums, int target) { 左右边界计算完之后,看一下主体代码,这里把上面讨论的三种情况,都覆盖了 -```C++ +```CPP class Solution { public: vector searchRange(vector& nums, int target) { @@ -173,11 +173,219 @@ private: ## Java ```java +class Solution { + int[] searchRange(int[] nums, int target) { + int leftBorder = getLeftBorder(nums, target); + int rightBorder = getRightBorder(nums, target); + // 情况一 + if (leftBorder == -2 || rightBorder == -2) return new int[]{-1, -1}; + // 情况三 + if (rightBorder - leftBorder > 1) return new int[]{leftBorder + 1, rightBorder - 1}; + // 情况二 + return new int[]{-1, -1}; + } + + int getRightBorder(int[] nums, int target) { + int left = 0; + int right = nums.length - 1; + int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况 + while (left <= right) { + int middle = left + ((right - left) / 2); + if (nums[middle] > target) { + right = middle - 1; + } else { // 寻找右边界,nums[middle] == target的时候更新left + left = middle + 1; + rightBorder = left; + } + } + return rightBorder; + } + + int getLeftBorder(int[] nums, int target) { + int left = 0; + int right = nums.length - 1; + int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况 + while (left <= right) { + int middle = left + ((right - left) / 2); + if (nums[middle] >= target) { // 寻找左边界,nums[middle] == target的时候更新right + right = middle - 1; + leftBorder = right; + } else { + left = middle + 1; + } + } + return leftBorder; + } +} ``` +```java +// 解法2 +// 1、首先,在 nums 数组中二分查找 target; +// 2、如果二分查找失败,则 binarySearch 返回 -1,表明 nums 中没有 target。此时,searchRange 直接返回 {-1, -1}; +// 3、如果二分查找成功,则 binarySearch 返回 nums 中值为 target 的一个下标。然后,通过左右滑动指针,来找到符合题意的区间 + +class Solution { + public int[] searchRange(int[] nums, int target) { + int index = binarySearch(nums, target); // 二分查找 + + if (index == -1) { // nums 中不存在 target,直接返回 {-1, -1} + return new int[] {-1, -1}; // 匿名数组 + } + // nums 中存在 targe,则左右滑动指针,来找到符合题意的区间 + int left = index; + int right = index; + // 向左滑动,找左边界 + while (left - 1 >= 0 && nums[left - 1] == nums[index]) { // 防止数组越界。逻辑短路,两个条件顺序不能换 + left--; + } + // 向左滑动,找右边界 + while (right + 1 < nums.length && nums[right + 1] == nums[index]) { // 防止数组越界。 + right++; + } + return new int[] {left, right}; + } + + /** + * 二分查找 + * @param nums + * @param target + */ + public int binarySearch(int[] nums, int target) { + int left = 0; + int right = nums.length - 1; // 不变量:左闭右闭区间 + + while (left <= right) { // 不变量:左闭右闭区间 + int mid = left + (right - left) / 2; + if (nums[mid] == target) { + return mid; + } else if (nums[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; // 不变量:左闭右闭区间 + } + } + return -1; // 不存在 + } +} +``` + + + ## Python ```python +class Solution: + def searchRange(self, nums: List[int], target: int) -> List[int]: + def getRightBorder(nums:List[int], target:int) -> int: + left, right = 0, len(nums)-1 + rightBoder = -2 # 记录一下rightBorder没有被赋值的情况 + while left <= right: + middle = left + (right-left) // 2 + if nums[middle] > target: + right = middle - 1 + else: # 寻找右边界,nums[middle] == target的时候更新left + left = middle + 1 + rightBoder = left + + return rightBoder + + def getLeftBorder(nums:List[int], target:int) -> int: + left, right = 0, len(nums)-1 + leftBoder = -2 # 记录一下leftBorder没有被赋值的情况 + while left <= right: + middle = left + (right-left) // 2 + if nums[middle] >= target: # 寻找左边界,nums[middle] == target的时候更新right + right = middle - 1; + leftBoder = right; + else: + left = middle + 1 + return leftBoder + leftBoder = getLeftBorder(nums, target) + rightBoder = getRightBorder(nums, target) + # 情况一 + if leftBoder == -2 or rightBoder == -2: return [-1, -1] + # 情况三 + if rightBoder -leftBoder >1: return [leftBoder + 1, rightBoder - 1] + # 情况二 + return [-1, -1] +``` +```python +# 解法2 +# 1、首先,在 nums 数组中二分查找 target; +# 2、如果二分查找失败,则 binarySearch 返回 -1,表明 nums 中没有 target。此时,searchRange 直接返回 {-1, -1}; +# 3、如果二分查找成功,则 binarySearch 返回 nums 中值为 target 的一个下标。然后,通过左右滑动指针,来找到符合题意的区间 +class Solution: + def searchRange(self, nums: List[int], target: int) -> List[int]: + def binarySearch(nums:List[int], target:int) -> int: + left, right = 0, len(nums)-1 + while left<=right: # 不变量:左闭右闭区间 + middle = left + (right-left) // 2 + if nums[middle] > target: + right = middle - 1 + elif nums[middle] < target: + left = middle + 1 + else: + return middle + return -1 + index = binarySearch(nums, target) + if index == -1:return [-1, -1] # nums 中不存在 target,直接返回 {-1, -1} + # nums 中存在 targe,则左右滑动指针,来找到符合题意的区间 + left, right = index, index + # 向左滑动,找左边界 + while left -1 >=0 and nums[left - 1] == target: left -=1 + # 向右滑动,找右边界 + while right+1 < len(nums) and nums[right + 1] == target: right +=1 + return [left, right] +``` +```python +# 解法3 +# 1、首先,在 nums 数组中二分查找得到第一个大于等于 target的下标(左边界)与第一个大于target的下标(右边界); +# 2、如果左边界<= 右边界,则返回 [左边界, 右边界]。否则返回[-1, -1] +class Solution: + def searchRange(self, nums: List[int], target: int) -> List[int]: + def binarySearch(nums:List[int], target:int, lower:bool) -> int: + left, right = 0, len(nums)-1 + ans = len(nums) + while left<=right: # 不变量:左闭右闭区间 + middle = left + (right-left) //2 + # lower为True,执行前半部分,找到第一个大于等于 target的下标 ,否则找到第一个大于target的下标 + if nums[middle] > target or (lower and nums[middle] >= target): + right = middle - 1 + ans = middle + else: + left = middle + 1 + return ans + + leftBorder = binarySearch(nums, target, True) # 搜索左边界 + rightBorder = binarySearch(nums, target, False) -1 # 搜索右边界 + if leftBorder<= rightBorder and rightBorder< len(nums) and nums[leftBorder] == target and nums[rightBorder] == target: + return [leftBorder, rightBorder] + return [-1, -1] +``` + +```python +# 解法4 +# 1、首先,在 nums 数组中二分查找得到第一个大于等于 target的下标leftBorder; +# 2、在 nums 数组中二分查找得到第一个大于等于 target+1的下标, 减1则得到rightBorder; +# 3、如果开始位置在数组的右边或者不存在target,则返回[-1, -1] 。否则返回[leftBorder, rightBorder] +class Solution: + def searchRange(self, nums: List[int], target: int) -> List[int]: + def binarySearch(nums:List[int], target:int) -> int: + left, right = 0, len(nums)-1 + while left<=right: # 不变量:左闭右闭区间 + middle = left + (right-left) //2 + if nums[middle] >= target: + right = middle - 1 + else: + left = middle + 1 + return left # 若存在target,则返回第一个等于target的值 + + leftBorder = binarySearch(nums, target) # 搜索左边界 + rightBorder = binarySearch(nums, target+1) -1 # 搜索右边界 + if leftBorder == len(nums) or nums[leftBorder]!= target: # 情况一和情况二 + return [-1, -1] + return [leftBorder, rightBorder] ``` ## Go @@ -194,6 +402,5 @@ private: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
- +
diff --git a/problems/0035.搜索插入位置.md b/problems/0035.搜索插入位置.md index 0f826412..061573e0 100644 --- a/problems/0035.搜索插入位置.md +++ b/problems/0035.搜索插入位置.md @@ -116,7 +116,7 @@ public: **大家要仔细看注释,思考为什么要写while(left <= right), 为什么要写right = middle - 1**。 -```C++ +```CPP class Solution { public: int searchInsert(vector& nums, int target) { @@ -158,7 +158,7 @@ public: **大家要仔细看注释,思考为什么要写while (left < right), 为什么要写right = middle**。 -```cpp +```CPP class Solution { public: int searchInsert(vector& nums, int target) { @@ -234,8 +234,6 @@ class Solution { ``` - - Python: ```python3 class Solution: @@ -254,9 +252,6 @@ class Solution: return right + 1 ``` - -Go: - JavaScript: ```js var searchInsert = function (nums, target) { @@ -277,9 +272,45 @@ var searchInsert = function (nums, target) { }; ``` +Swift: + +```swift +// 暴力法 +func searchInsert(_ nums: [Int], _ target: Int) -> Int { + for i in 0..= target { + return i + } + } + return nums.count +} + +// 二分法 +func searchInsert(_ nums: [Int], _ target: Int) -> Int { + var left = 0 + var right = nums.count - 1 + + while left <= right { + let middle = left + ((right - left) >> 1) + + if nums[middle] > target { + right = middle - 1 + }else if nums[middle] < target { + left = middle + 1 + }else if nums[middle] == target { + return middle + } + } + + return right + 1 +} +``` + + + ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0037.解数独.md b/problems/0037.解数独.md index e43708b8..64be7013 100644 --- a/problems/0037.解数独.md +++ b/problems/0037.解数独.md @@ -90,7 +90,7 @@ bool backtracking(vector>& board) 代码如下:(**详细看注释**) -```C++ +```CPP bool backtracking(vector>& board) { for (int i = 0; i < board.size(); i++) { // 遍历行 for (int j = 0; j < board[0].size(); j++) { // 遍历列 @@ -125,7 +125,7 @@ bool backtracking(vector>& board) { 代码如下: -```C++ +```CPP bool isValid(int row, int col, char val, vector>& board) { for (int i = 0; i < 9; i++) { // 判断行里是否重复 if (board[row][i] == val) { @@ -154,7 +154,7 @@ bool isValid(int row, int col, char val, vector>& board) { ## C++代码 -```C++ +```CPP class Solution { private: bool backtracking(vector>& board) { @@ -437,4 +437,4 @@ var solveSudoku = function(board) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0039.组合总和.md b/problems/0039.组合总和.md index ba8128b5..0c452e22 100644 --- a/problems/0039.组合总和.md +++ b/problems/0039.组合总和.md @@ -73,7 +73,7 @@ candidates 中的数字可以无限制重复被选取。 代码如下: -```C++ +```CPP vector> result; vector path; void backtracking(vector& candidates, int target, int sum, int startIndex) @@ -89,7 +89,7 @@ void backtracking(vector& candidates, int target, int sum, int startIndex) sum等于target的时候,需要收集结果,代码如下: -```C++ +```CPP if (sum > target) { return; } @@ -107,7 +107,7 @@ if (sum == target) { 如何重复选取呢,看代码,注释部分: -```C++ +```CPP for (int i = startIndex; i < candidates.size(); i++) { sum += candidates[i]; path.push_back(candidates[i]); @@ -119,7 +119,7 @@ for (int i = startIndex; i < candidates.size(); i++) { 按照[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)中给出的模板,不难写出如下C++完整代码: -```C++ +```CPP // 版本一 class Solution { private: @@ -179,7 +179,7 @@ for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; 整体代码如下:(注意注释的部分) -```C++ +```CPP class Solution { private: vector> result; @@ -351,4 +351,4 @@ var combinationSum = function(candidates, target) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0040.组合总和II.md b/problems/0040.组合总和II.md index 70e012da..5ea11ca7 100644 --- a/problems/0040.组合总和II.md +++ b/problems/0040.组合总和II.md @@ -90,7 +90,7 @@ candidates 中的每个数字在每个组合中只能使用一次。 代码如下: -```C++ +```CPP vector> result; // 存放组合集合 vector path; // 符合条件的组合 void backtracking(vector& candidates, int target, int sum, int startIndex, vector& used) { @@ -102,7 +102,7 @@ void backtracking(vector& candidates, int target, int sum, int startIndex, 代码如下: -```C++ +```CPP if (sum > target) { // 这个条件其实可以省略 return; } @@ -137,7 +137,7 @@ if (sum == target) { 那么单层搜索的逻辑代码如下: -```C++ +```CPP for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) { // used[i - 1] == true,说明同一树支candidates[i - 1]使用过 // used[i - 1] == false,说明同一树层candidates[i - 1]使用过 @@ -161,7 +161,7 @@ for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; 回溯三部曲分析完了,整体C++代码如下: -```C++ +```CPP class Solution { private: vector> result; @@ -206,7 +206,7 @@ public: 这里直接用startIndex来去重也是可以的, 就不用used数组了。 -```C++ +```CPP class Solution { private: vector> result; @@ -398,4 +398,4 @@ var combinationSum2 = function(candidates, target) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0042.接雨水.md b/problems/0042.接雨水.md index 3f4d0123..a2cb2345 100644 --- a/problems/0042.接雨水.md +++ b/problems/0042.接雨水.md @@ -77,7 +77,7 @@ 一样的方法,只要从头遍历一遍所有的列,然后求出每一列雨水的体积,相加之后就是总雨水的体积了。 首先从头遍历所有的列,并且**要注意第一个柱子和最后一个柱子不接雨水**,代码如下: -```C++ +```CPP for (int i = 0; i < height.size(); i++) { // 第一个柱子和最后一个柱子不接雨水 if (i == 0 || i == height.size() - 1) continue; @@ -86,7 +86,7 @@ for (int i = 0; i < height.size(); i++) { 在for循环中求左右两边最高柱子,代码如下: -```C++ +```CPP int rHeight = height[i]; // 记录右边柱子的最高高度 int lHeight = height[i]; // 记录左边柱子的最高高度 for (int r = i + 1; r < height.size(); r++) { @@ -99,14 +99,14 @@ for (int l = i - 1; l >= 0; l--) { 最后,计算该列的雨水高度,代码如下: -```C++ +```CPP int h = min(lHeight, rHeight) - height[i]; if (h > 0) sum += h; // 注意只有h大于零的时候,在统计到总和中 ``` 整体代码如下: -```C++ +```CPP class Solution { public: int trap(vector& height) { @@ -152,7 +152,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: int trap(vector& height) { @@ -287,7 +287,7 @@ if (height[i] == height[st.top()]) { // 例如 5 5 1 7 这种情况 求当前凹槽雨水的体积代码如下: -```C++ +```CPP while (!st.empty() && height[i] > height[st.top()]) { // 注意这里是while,持续跟新栈顶元素 int mid = st.top(); st.pop(); @@ -301,7 +301,7 @@ while (!st.empty() && height[i] > height[st.top()]) { // 注意这里是while, 关键部分讲完了,整体代码如下: -```C++ +```CPP class Solution { public: int trap(vector& height) { @@ -335,7 +335,7 @@ public: 以上代码冗余了一些,但是思路是清晰的,下面我将代码精简一下,如下: -```C++ +```CPP class Solution { public: int trap(vector& height) { @@ -388,6 +388,44 @@ class Solution: res += res1 return res ``` +动态规划 +```python3 +class Solution: + def trap(self, height: List[int]) -> int: + leftheight, rightheight = [0]*len(height), [0]*len(height) + + leftheight[0]=height[0] + for i in range(1,len(height)): + leftheight[i]=max(leftheight[i-1],height[i]) + rightheight[-1]=height[-1] + for i in range(len(height)-2,-1,-1): + rightheight[i]=max(rightheight[i+1],height[i]) + + result = 0 + for i in range(0,len(height)): + summ = min(leftheight[i],rightheight[i])-height[i] + result += summ + return result +``` +单调栈 +```python3 +class Solution: + def trap(self, height: List[int]) -> int: + st =[0] + result = 0 + for i in range(1,len(height)): + while st!=[] and height[i]>height[st[-1]]: + midh = height[st[-1]] + st.pop() + if st!=[]: + hright = height[i] + hleft = height[st[-1]] + h = min(hright,hleft)-midh + w = i-st[-1]-1 + result+=h*w + st.append(i) + return result +``` Go: @@ -398,4 +436,4 @@ JavaScript: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0045.跳跃游戏II.md b/problems/0045.跳跃游戏II.md index a161d944..d946315b 100644 --- a/problems/0045.跳跃游戏II.md +++ b/problems/0045.跳跃游戏II.md @@ -63,7 +63,7 @@ C++代码如下:(详细注释) -```C++ +```CPP // 版本一 class Solution { public: @@ -106,7 +106,7 @@ public: 代码如下: -```C++ +```CPP // 版本二 class Solution { public: @@ -236,4 +236,4 @@ var jump = function(nums) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0046.全排列.md b/problems/0046.全排列.md index 3ee7271e..c0626fb7 100644 --- a/problems/0046.全排列.md +++ b/problems/0046.全排列.md @@ -106,7 +106,7 @@ for (int i = 0; i < nums.size(); i++) { 整体C++代码如下: -```C++ +```CPP class Solution { public: vector> result; @@ -227,24 +227,27 @@ class Solution: Go: ```Go -var result [][]int -func backtrack(nums,pathNums []int,used []bool){ - if len(nums)==len(pathNums){ - tmp:=make([]int,len(nums)) - copy(tmp,pathNums) - result=append(result,tmp) - //result=append(result,pathNums) - return - } - for i:=0;i +
diff --git a/problems/0047.全排列II.md b/problems/0047.全排列II.md index 66107c95..61428299 100644 --- a/problems/0047.全排列II.md +++ b/problems/0047.全排列II.md @@ -228,35 +228,31 @@ Go: ```go var res [][]int func permute(nums []int) [][]int { - res = [][]int{} - sort.Ints(nums) - dfs(nums, make([]int, 0), make([]bool, len(nums))) - return res + res = [][]int{} + backTrack(nums,len(nums),[]int{}) + return res } +func backTrack(nums []int,numsLen int,path []int) { + if len(nums)==0{ + p:=make([]int,len(path)) + copy(p,path) + res = append(res,p) + } + used := [21]int{}//跟前一题唯一的区别,同一层不使用重复的数。关于used的思想carl在递增子序列那一题中提到过 + for i:=0;i +
diff --git a/problems/0051.N皇后.md b/problems/0051.N皇后.md index a8919ec3..387e75c3 100644 --- a/problems/0051.N皇后.md +++ b/problems/0051.N皇后.md @@ -171,7 +171,7 @@ bool isValid(int row, int col, vector& chessboard, int n) { ## C++代码 -```C++ +```CPP class Solution { private: vector> result; @@ -490,4 +490,4 @@ var solveNQueens = function(n) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0053.最大子序和.md b/problems/0053.最大子序和.md index 81e0b35a..cba7d6c7 100644 --- a/problems/0053.最大子序和.md +++ b/problems/0053.最大子序和.md @@ -25,7 +25,7 @@ 时间复杂度:O(n^2) 空间复杂度:O(1) -```C++ +```CPP class Solution { public: int maxSubArray(vector& nums) { @@ -81,7 +81,7 @@ if (count > result) result = count; 那么不难写出如下C++代码(关键地方已经注释) -```C++ +```CPP class Solution { public: int maxSubArray(vector& nums) { @@ -109,7 +109,7 @@ public: 那么先给出我的dp代码如下,有时间的录友可以提前做一做: -```C++ +```CPP class Solution { public: int maxSubArray(vector& nums) { @@ -214,4 +214,4 @@ var maxSubArray = function(nums) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0053.最大子序和(动态规划).md b/problems/0053.最大子序和(动态规划).md index dd0e513b..0932c77f 100644 --- a/problems/0053.最大子序和(动态规划).md +++ b/problems/0053.最大子序和(动态规划).md @@ -65,7 +65,7 @@ dp[0]应该是多少呢? 以上动规五部曲分析完毕,完整代码如下: -```C++ +```CPP class Solution { public: int maxSubArray(vector& nums) { @@ -138,7 +138,52 @@ class Solution: ``` Go: +```Go +// solution +// 1, dp +// 2, 贪心 +func maxSubArray(nums []int) int { + n := len(nums) + // 这里的dp[i] 表示,最大的连续子数组和,包含num[i] 元素 + dp := make([]int,n) + // 初始化,由于dp 状态转移方程依赖dp[0] + dp[0] = nums[0] + // 初始化最大的和 + mx := nums[0] + for i:=1;ib { + return a + } + return b +} +``` + +JavaScript: + +```javascript +const maxSubArray = nums => { + // 数组长度,dp初始化 + const [len, dp] = [nums.length, [nums[0]]]; + // 最大值初始化为dp[0] + let max = dp[0]; + for (let i = 1; i < len; i++) { + dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]); + // 更新最大值 + max = Math.max(max, dp[i]); + } + return max; +}; +``` @@ -146,4 +191,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0055.跳跃游戏.md b/problems/0055.跳跃游戏.md index 8618515e..0b1c1d36 100644 --- a/problems/0055.跳跃游戏.md +++ b/problems/0055.跳跃游戏.md @@ -58,7 +58,7 @@ i每次移动只能在cover的范围内移动,每移动一个元素,cover得 C++代码如下: -```C++ +```CPP class Solution { public: bool canJump(vector& nums) { @@ -161,4 +161,4 @@ var canJump = function(nums) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0056.合并区间.md b/problems/0056.合并区间.md index d4ffc554..441cc63c 100644 --- a/problems/0056.合并区间.md +++ b/problems/0056.合并区间.md @@ -56,7 +56,7 @@ C++代码如下: -```C++ +```CPP class Solution { public: // 按照区间左边界从小到大排序 @@ -92,7 +92,7 @@ public: 当然以上代码有冗余一些,可以优化一下,如下:(思路是一样的) -```C++ +```CPP class Solution { public: vector> merge(vector>& intervals) { @@ -176,30 +176,27 @@ class Solution: ``` Go: -```Go +```golang func merge(intervals [][]int) [][]int { - sort.Slice(intervals, func(i, j int) bool { - return intervals[i][0]=intervals[i+1][0]{ + intervals[i][1]=max(intervals[i][1],intervals[i+1][1])//赋值最大值 + intervals=append(intervals[:i+1],intervals[i+2:]...) + i-- + } + } + return intervals } -func max(a, b int) int { - if a > b { return a } - return b +func max(a,b int)int{ + if a>b{ + return a + } + return b } ``` @@ -229,4 +226,4 @@ var merge = function (intervals) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0059.螺旋矩阵II.md b/problems/0059.螺旋矩阵II.md index 90b74005..6df8c83d 100644 --- a/problems/0059.螺旋矩阵II.md +++ b/problems/0059.螺旋矩阵II.md @@ -66,7 +66,7 @@ 整体C++代码如下: -```C++ +```CPP class Solution { public: vector> generateMatrix(int n) { @@ -302,6 +302,61 @@ func generateMatrix(n int) [][]int { } ``` +Swift: + +```swift +func generateMatrix(_ n: Int) -> [[Int]] { + var result = [[Int]](repeating: [Int](repeating: 0, count: n), count: n) + + var startRow = 0 + var startColumn = 0 + var loopCount = n / 2 + let mid = n / 2 + var count = 1 + var offset = 1 + var row: Int + var column: Int + + while loopCount > 0 { + row = startRow + column = startColumn + + for c in column ..< startColumn + n - offset { + result[startRow][c] = count + count += 1 + column += 1 + } + + for r in row ..< startRow + n - offset { + result[r][column] = count + count += 1 + row += 1 + } + + for _ in startColumn ..< column { + result[row][column] = count + count += 1 + column -= 1 + } + + for _ in startRow ..< row { + result[row][column] = count + count += 1 + row -= 1 + } + + startRow += 1 + startColumn += 1 + offset += 2 + loopCount -= 1 + } + + if (n % 2) != 0 { + result[mid][mid] = count + } + return result +} +``` @@ -309,4 +364,4 @@ func generateMatrix(n int) [][]int { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0062.不同路径.md b/problems/0062.不同路径.md index 50e70b3e..84c84075 100644 --- a/problems/0062.不同路径.md +++ b/problems/0062.不同路径.md @@ -59,7 +59,7 @@ 此时问题就可以转化为求二叉树叶子节点的个数,代码如下: -```C++ +```CPP class Solution { private: int dfs(int i, int j, int m, int n) { @@ -128,7 +128,7 @@ for (int j = 0; j < n; j++) dp[0][j] = 1; 以上动规五部曲分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int uniquePaths(int m, int n) { @@ -149,7 +149,7 @@ public: 其实用一个一维数组(也可以理解是滚动数组)就可以了,但是不利于理解,可以优化点空间,建议先理解了二维,在理解一维,C++代码如下: -```C++ +```CPP class Solution { public: int uniquePaths(int m, int n) { @@ -187,7 +187,7 @@ public: 例如如下代码是不行的。 -```C++ +```CPP class Solution { public: int uniquePaths(int m, int n) { @@ -204,7 +204,7 @@ public: 需要在计算分子的时候,不断除以分母,代码如下: -```C++ +```CPP class Solution { public: int uniquePaths(int m, int n) { @@ -333,4 +333,4 @@ var uniquePaths = function(m, n) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0063.不同路径II.md b/problems/0063.不同路径II.md index a61ffd02..3df1b301 100644 --- a/problems/0063.不同路径II.md +++ b/problems/0063.不同路径II.md @@ -97,7 +97,7 @@ for (int j = 0; j < n; j++) dp[0][j] = 1; 所以本题初始化代码为: -```C++ +```CPP vector> dp(m, vector(n, 0)); for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) dp[i][0] = 1; for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) dp[0][j] = 1; @@ -111,7 +111,7 @@ for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) dp[0][j] = 1; 代码如下: -```C++ +```CPP for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { if (obstacleGrid[i][j] == 1) continue; @@ -135,7 +135,7 @@ for (int i = 1; i < m; i++) { 动规五部分分析完毕,对应C++代码如下: -```C++ +```CPP class Solution { public: int uniquePathsWithObstacles(vector>& obstacleGrid) { @@ -341,4 +341,4 @@ var uniquePathsWithObstacles = function(obstacleGrid) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0070.爬楼梯.md b/problems/0070.爬楼梯.md index 96899d37..bb9a757c 100644 --- a/problems/0070.爬楼梯.md +++ b/problems/0070.爬楼梯.md @@ -109,7 +109,7 @@ dp[i]: 爬到第i层楼梯,有dp[i]种方法 以上五部分析完之后,C++代码如下: -```C++ +```CPP // 版本一 class Solution { public: @@ -130,7 +130,7 @@ public: 当然依然也可以,优化一下空间复杂度,代码如下: -```C++ +```CPP // 版本二 class Solution { public: @@ -163,7 +163,7 @@ public: 这里我先给出我的实现代码: -```C++ +```CPP class Solution { public: int climbStairs(int n) { @@ -301,4 +301,4 @@ var climbStairs = function(n) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0070.爬楼梯完全背包版本.md b/problems/0070.爬楼梯完全背包版本.md index b3e7eb73..f7a80f3b 100644 --- a/problems/0070.爬楼梯完全背包版本.md +++ b/problems/0070.爬楼梯完全背包版本.md @@ -193,4 +193,4 @@ func climbStairs(n int) int { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0072.编辑距离.md b/problems/0072.编辑距离.md index 9ddca7c0..8ec0a65b 100644 --- a/problems/0072.编辑距离.md +++ b/problems/0072.编辑距离.md @@ -91,18 +91,18 @@ if (word1[i - 1] != word2[j - 1]) `if (word1[i - 1] != word2[j - 1])`,此时就需要编辑了,如何编辑呢? -操作一:word1增加一个元素,使其word1[i - 1]与word2[j - 1]相同,那么就是以下标i-2为结尾的word1 与 j-1为结尾的word2的最近编辑距离 加上一个增加元素的操作。 +* 操作一:word1删除一个元素,那么就是以下标i - 2为结尾的word1 与 j-1为结尾的word2的最近编辑距离 再加上一个操作。 即 `dp[i][j] = dp[i - 1][j] + 1;` -操作二:word2添加一个元素,使其word1[i - 1]与word2[j - 1]相同,那么就是以下标i-1为结尾的word1 与 j-2为结尾的word2的最近编辑距离 加上一个增加元素的操作。 +* 操作二:word2删除一个元素,那么就是以下标i - 1为结尾的word1 与 j-2为结尾的word2的最近编辑距离 再加上一个操作。 即 `dp[i][j] = dp[i][j - 1] + 1;` -这里有同学发现了,怎么都是添加元素,删除元素去哪了。 +这里有同学发现了,怎么都是删除元素,添加元素去哪了。 -**word2添加一个元素,相当于word1删除一个元素**,例如 `word1 = "ad" ,word2 = "a"`,`word1`删除元素`'d'`,`word2`添加一个元素`'d'`,变成`word1="a", word2="ad"`, 最终的操作数是一样! dp数组如下图所示意的: +**word2添加一个元素,相当于word1删除一个元素**,例如 `word1 = "ad" ,word2 = "a"`,`word1`删除元素`'d'` 和 `word2`添加一个元素`'d'`,变成`word1="a", word2="ad"`, 最终的操作数是一样! dp数组如下图所示意的: ``` a a d @@ -123,7 +123,7 @@ if (word1[i - 1] != word2[j - 1]) 递归公式代码如下: -```C++ +```CPP if (word1[i - 1] == word2[j - 1]) { dp[i][j] = dp[i - 1][j - 1]; } @@ -151,7 +151,7 @@ dp[i][0] :以下标i-1为结尾的字符串word1,和空字符串word2,最 所以C++代码如下: -```C++ +```CPP for (int i = 0; i <= word1.size(); i++) dp[i][0] = i; for (int j = 0; j <= word2.size(); j++) dp[0][j] = j; ``` @@ -175,7 +175,7 @@ for (int j = 0; j <= word2.size(); j++) dp[0][j] = j; 代码如下: -```C++ +```CPP for (int i = 1; i <= word1.size(); i++) { for (int j = 1; j <= word2.size(); j++) { if (word1[i - 1] == word2[j - 1]) { @@ -198,7 +198,7 @@ for (int i = 1; i <= word1.size(); i++) { 以上动规五部分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int minDistance(string word1, string word2) { @@ -338,4 +338,4 @@ const minDistance = (word1, word2) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0077.组合.md b/problems/0077.组合.md index 0b289a40..d88a5711 100644 --- a/problems/0077.组合.md +++ b/problems/0077.组合.md @@ -173,7 +173,7 @@ for循环每次从startIndex开始遍历,然后用path保存取到的节点i 代码如下: -```C++ +```CPP for (int i = startIndex; i <= n; i++) { // 控制树的横向遍历 path.push_back(i); // 处理节点 backtracking(n, k, i + 1); // 递归:控制树的纵向遍历,注意下一层搜索要从i+1开始 @@ -188,7 +188,7 @@ backtracking的下面部分就是回溯的操作了,撤销本次处理的结 关键地方都讲完了,组合问题C++完整代码如下: -```C++ +```CPP class Solution { private: vector> result; // 存放符合条件结果的集合 @@ -440,4 +440,4 @@ func backtrack(n,k,start int,track []int){ * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0077.组合优化.md b/problems/0077.组合优化.md index d3e82f09..d545543d 100644 --- a/problems/0077.组合优化.md +++ b/problems/0077.组合优化.md @@ -249,4 +249,4 @@ var combine = function(n, k) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0078.子集.md b/problems/0078.子集.md index 0b2f3c09..ca49f97b 100644 --- a/problems/0078.子集.md +++ b/problems/0078.子集.md @@ -120,7 +120,7 @@ void backtracking(参数) { 可以写出如下回溯算法C++代码: -```C++ +```CPP class Solution { private: vector> result; @@ -268,4 +268,4 @@ var subsets = function(nums) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0084.柱状图中最大的矩形.md b/problems/0084.柱状图中最大的矩形.md new file mode 100644 index 00000000..941888db --- /dev/null +++ b/problems/0084.柱状图中最大的矩形.md @@ -0,0 +1,247 @@ + + +# 84.柱状图中最大的矩形 + +链接:https://leetcode-cn.com/problems/largest-rectangle-in-histogram/ + +给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 + +求在该柱状图中,能够勾勒出来的矩形的最大面积。 + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20210803220437.png) + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20210803220506.png) + + +# 思路 + +本题和[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw),是遥相呼应的两道题目,建议都要仔细做一做,原理上有很多相同的地方,但细节上又有差异,更可以加深对单调栈的理解! + +其实这两道题目先做那一道都可以,但我先写的42.接雨水的题解,所以如果没做过接雨水的话,建议先做一做接雨水,可以参考我的题解:[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw) + +我们先来看一下双指针的解法: + +## 双指针解法 + +```CPP +class Solution { +public: + int largestRectangleArea(vector& heights) { + int sum = 0; + for (int i = 0; i < heights.size(); i++) { + int left = i; + int right = i; + for (; left >= 0; left--) { + if (heights[left] < heights[i]) break; + } + for (; right < heights.size(); right++) { + if (heights[right] < heights[i]) break; + } + int w = right - left - 1; + int h = heights[i]; + sum = max(sum, w * h); + } + return sum; + } +}; +``` + +如上代码并不能通过leetcode,超时了,因为时间复杂度是O(n^2)。 + +## 动态规划 + +本题动态规划的写法整体思路和[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw)是一致的,但要比[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw)难一些。 + +难就难在本题要记录记录每个柱子 左边第一个小于该柱子的下标,而不是左边第一个小于该柱子的高度。 + +所以需要循环查找,也就是下面在寻找的过程中使用了while,详细请看下面注释,整理思路在题解:[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw)中已经介绍了。 + +```CPP +class Solution { +public: + int largestRectangleArea(vector& heights) { + vector minLeftIndex(heights.size()); + vector minRightIndex(heights.size()); + int size = heights.size(); + + // 记录每个柱子 左边第一个小于该柱子的下标 + minLeftIndex[0] = -1; // 注意这里初始化,防止下面while死循环 + for (int i = 1; i < size; i++) { + int t = i - 1; + // 这里不是用if,而是不断向左寻找的过程 + while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t]; + minLeftIndex[i] = t; + } + // 记录每个柱子 右边第一个小于该柱子的下标 + minRightIndex[size - 1] = size; // 注意这里初始化,防止下面while死循环 + for (int i = size - 2; i >= 0; i--) { + int t = i + 1; + // 这里不是用if,而是不断向右寻找的过程 + while (t < size && heights[t] >= heights[i]) t = minRightIndex[t]; + minRightIndex[i] = t; + } + // 求和 + int result = 0; + for (int i = 0; i < size; i++) { + int sum = heights[i] * (minRightIndex[i] - minLeftIndex[i] - 1); + result = max(sum, result); + } + return result; + } +}; +``` + +## 单调栈 + +本地单调栈的解法和接雨水的题目是遥相呼应的。 + +为什么这么说呢,[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw)是找每个柱子左右两边第一个大于该柱子高度的柱子,而本题是找每个柱子左右两边第一个小于该柱子的柱子。 + +**这里就涉及到了单调栈很重要的性质,就是单调栈里的顺序,是从小到大还是从大到小**。 + +在题解[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw)中我讲解了接雨水的单调栈从栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。 + +那么因为本题是要找每个柱子左右两边第一个小于该柱子的柱子,所以从栈头(元素从栈头弹出)到栈底的顺序应该是从大到小的顺序! + +我来举一个例子,如图: + +![84.柱状图中最大的矩形](https://img-blog.csdnimg.cn/20210223155303971.jpg) + +只有栈里从大到小的顺序,才能保证栈顶元素找到左右两边第一个小于栈顶元素的柱子。 + +所以本题单调栈的顺序正好与接雨水反过来。 + +此时大家应该可以发现其实就是**栈顶和栈顶的下一个元素以及要入栈的三个元素组成了我们要求最大面积的高度和宽度** + +理解这一点,对单调栈就掌握的比较到位了。 + +除了栈内元素顺序和接雨水不同,剩下的逻辑就都差不多了,在题解[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw)我已经对单调栈的各个方面做了详细讲解,这里就不赘述了。 + +剩下就是分析清楚如下三种情况: + +* 情况一:当前遍历的元素heights[i]小于栈顶元素heights[st.top()]的情况 +* 情况二:当前遍历的元素heights[i]等于栈顶元素heights[st.top()]的情况 +* 情况三:当前遍历的元素heights[i]大于栈顶元素heights[st.top()]的情况 + +C++代码如下: + +```CPP +// 版本一 +class Solution { +public: + int largestRectangleArea(vector& heights) { + stack st; + heights.insert(heights.begin(), 0); // 数组头部加入元素0 + heights.push_back(0); // 数组尾部加入元素0 + st.push(0); + int result = 0; + // 第一个元素已经入栈,从下表1开始 + for (int i = 1; i < heights.size(); i++) { + // 注意heights[i] 是和heights[st.top()] 比较 ,st.top()是下表 + if (heights[i] > heights[st.top()]) { + st.push(i); + } else if (heights[i] == heights[st.top()]) { + st.pop(); // 这个可以加,可以不加,效果一样,思路不同 + st.push(i); + } else { + while (heights[i] < heights[st.top()]) { // 注意是while + int mid = st.top(); + st.pop(); + int left = st.top(); + int right = i; + int w = right - left - 1; + int h = heights[mid]; + result = max(result, w * h); + } + st.push(i); + } + } + return result; + } +}; + +``` + +代码精简之后: + +```CPP +// 版本二 +class Solution { +public: + int largestRectangleArea(vector& heights) { + stack st; + heights.insert(heights.begin(), 0); // 数组头部加入元素0 + heights.push_back(0); // 数组尾部加入元素0 + st.push(0); + int result = 0; + for (int i = 1; i < heights.size(); i++) { + while (heights[i] < heights[st.top()]) { + int mid = st.top(); + st.pop(); + int w = i - st.top() - 1; + int h = heights[mid]; + result = max(result, w * h); + } + st.push(i); + } + return result; + } +}; +``` + +这里我依然建议大家按部就班把版本一写出来,把情况一二三分析清楚,然后在精简代码到版本二。 直接看版本二容易忽略细节! + +## 其他语言版本 + +Java: + +Python: + +动态规划 +```python3 +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + result = 0 + minleftindex, minrightindex = [0]*len(heights), [0]*len(heights) + + minleftindex[0]=-1 + for i in range(1,len(heights)): + t = i-1 + while t>=0 and heights[t]>=heights[i]: t=minleftindex[t] + minleftindex[i]=t + + minrightindex[-1]=len(heights) + for i in range(len(heights)-2,-1,-1): + t=i+1 + while t=heights[i]: t=minrightindex[t] + minrightindex[i]=t + + for i in range(0,len(heights)): + left = minleftindex[i] + right = minrightindex[i] + summ = (right-left-1)*heights[i] + result = max(result,summ) + return result +``` +单调栈 版本二 +```python3 +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + heights.insert(0,0) # 数组头部加入元素0 + heights.append(0) # 数组尾部加入元素0 + st = [0] + result = 0 + for i in range(1,len(heights)): + while st!=[] and heights[i] diff --git a/problems/0090.子集II.md b/problems/0090.子集II.md index 71aef5c7..5c19adc0 100644 --- a/problems/0090.子集II.md +++ b/problems/0090.子集II.md @@ -124,7 +124,7 @@ public: 代码如下: -```C++ +```CPP class Solution { private: vector> result; @@ -288,4 +288,4 @@ var subsetsWithDup = function(nums) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0093.复原IP地址.md b/problems/0093.复原IP地址.md index 40ad7684..9a95af22 100644 --- a/problems/0093.复原IP地址.md +++ b/problems/0093.复原IP地址.md @@ -183,7 +183,7 @@ void backtracking(参数) { 可以写出如下回溯算法C++代码: -```C++ +```CPP class Solution { private: vector result;// 记录结果 @@ -453,4 +453,4 @@ func isNormalIp(s string,startIndex,end int)bool{ * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0096.不同的二叉搜索树.md b/problems/0096.不同的二叉搜索树.md index 56f50d46..7cbe4fec 100644 --- a/problems/0096.不同的二叉搜索树.md +++ b/problems/0096.不同的二叉搜索树.md @@ -103,7 +103,7 @@ j相当于是头结点的元素,从1遍历到i为止。 代码如下: -```C++ +```CPP for (int i = 1; i <= n; i++) { for (int j = 1; j <= i; j++) { dp[i] += dp[j - 1] * dp[i - j]; @@ -123,7 +123,7 @@ n为5时候的dp数组状态如图: 综上分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int numTrees(int n) { @@ -234,4 +234,4 @@ const numTrees =(n) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0098.验证二叉搜索树.md b/problems/0098.验证二叉搜索树.md index 248d10f1..6f7e5c14 100644 --- a/problems/0098.验证二叉搜索树.md +++ b/problems/0098.验证二叉搜索树.md @@ -32,7 +32,7 @@ 可以递归中序遍历将二叉搜索树转变成一个数组,代码如下: -```C++ +```CPP vector vec; void traversal(TreeNode* root) { if (root == NULL) return; @@ -44,7 +44,7 @@ void traversal(TreeNode* root) { 然后只要比较一下,这个数组是否是有序的,**注意二叉搜索树中不能有重复元素**。 -```C++ +```CPP traversal(root); for (int i = 1; i < vec.size(); i++) { // 注意要小于等于,搜索树里不能有相同元素 @@ -55,7 +55,7 @@ return true; 整体代码如下: -```C++ +```CPP class Solution { private: vector vec; @@ -103,7 +103,7 @@ if (root->val > root->left->val && root->val < root->right->val) { ![二叉搜索树](https://img-blog.csdnimg.cn/20200812191501419.png) -节点10小于左节点5,大于右节点15,但右子树里出现了一个6 这就不符合了! +节点10大于左节点5,小于右节点15,但右子树里出现了一个6 这就不符合了! * 陷阱2 @@ -163,7 +163,7 @@ return left && right; 整体代码如下: -```C++ +```CPP class Solution { public: long long maxVal = LONG_MIN; // 因为后台测试数据中有int最小值 @@ -189,7 +189,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: TreeNode* pre = NULL; // 用来记录前一个节点 @@ -214,7 +214,7 @@ public: 迭代法中序遍历稍加改动就可以了,代码如下: -```C++ +```CPP class Solution { public: bool isValidBST(TreeNode* root) { @@ -337,6 +337,8 @@ class Solution { ``` Python: + +**递归** - 利用BST中序遍历特性,把树"压缩"成数组 ```python # Definition for a binary tree node. # class TreeNode: @@ -344,29 +346,56 @@ Python: # self.val = val # self.left = left # self.right = right -# 递归法 class Solution: def isValidBST(self, root: TreeNode) -> bool: - res = [] //把二叉搜索树按中序遍历写成list - def buildalist(root): - if not root: return - buildalist(root.left) //左 - res.append(root.val) //中 - buildalist(root.right) //右 - return res - buildalist(root) - return res == sorted(res) and len(set(res)) == len(res) //检查list里的数有没有重复元素,以及是否按从小到大排列 + # 思路: 利用BST中序遍历的特性. + # 中序遍历输出的二叉搜索树节点的数值是有序序列 + candidate_list = [] + + def __traverse(root: TreeNode) -> None: + nonlocal candidate_list + if not root: + return + __traverse(root.left) + candidate_list.append(root.val) + __traverse(root.right) + + def __is_sorted(nums: list) -> bool: + for i in range(1, len(nums)): + if nums[i] <= nums[i - 1]: # ⚠️ 注意: Leetcode定义二叉搜索树中不能有重复元素 + return False + return True + + __traverse(root) + res = __is_sorted(candidate_list) + + return res +``` -# 简单递归法 +**递归** - 标准做法 + +```python class Solution: def isValidBST(self, root: TreeNode) -> bool: - def isBST(root, min_val, max_val): - if not root: return True - if root.val >= max_val or root.val <= min_val: + # 规律: BST的中序遍历节点数值是从小到大. + cur_max = -float("INF") + def __isValidBST(root: TreeNode) -> bool: + nonlocal cur_max + + if not root: + return True + + is_left_valid = __isValidBST(root.left) + if cur_max < root.val: + cur_max = root.val + else: return False - return isBST(root.left, min_val, root.val) and isBST(root.right, root.val, max_val) - return isBST(root, float("-inf"), float("inf")) - + is_right_valid = __isValidBST(root.right) + + return is_left_valid and is_right_valid + return __isValidBST(root) +``` +``` # 迭代-中序遍历 class Solution: def isValidBST(self, root: TreeNode) -> bool: @@ -504,4 +533,4 @@ var isValidBST = function (root) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0100.相同的树.md b/problems/0100.相同的树.md index b536ffe3..df0a5dd1 100644 --- a/problems/0100.相同的树.md +++ b/problems/0100.相同的树.md @@ -61,7 +61,7 @@ bool compare(TreeNode* tree1, TreeNode* tree2) 此时tree1、tree2节点不为空,且数值也不相同的情况我们也处理了。 代码如下: -```C++ +```CPP if (tree1 == NULL && tree2 != NULL) return false; else if (tree1 != NULL && tree2 == NULL) return false; else if (tree1 == NULL && tree2 == NULL) return true; @@ -77,7 +77,7 @@ else if (tree1->val != tree2->val) return false; // 注意这里我没有 代码如下: -```C++ +```CPP bool left = compare(tree1->left, tree2->left); // 左子树:左、 右子树:左 bool right = compare(tree1->right, tree2->right); // 左子树:右、 右子树:右 bool isSame = left && right; // 左子树:中、 右子树:中(逻辑处理) @@ -85,7 +85,7 @@ return isSame; ``` 最后递归的C++整体代码如下: -```C++ +```CPP class Solution { public: bool compare(TreeNode* tree1, TreeNode* tree2) { @@ -119,7 +119,7 @@ public: ## 递归 -```C++ +```CPP class Solution { public: bool compare(TreeNode* left, TreeNode* right) { @@ -138,30 +138,30 @@ public: ## 迭代法 -```C++ -lass Solution { +```CPP +class Solution { public: bool isSameTree(TreeNode* p, TreeNode* q) { if (p == NULL && q == NULL) return true; if (p == NULL || q == NULL) return false; queue que; - que.push(p); // - que.push(q); // + que.push(p); // 添加根节点p + que.push(q); // 添加根节点q while (!que.empty()) { // TreeNode* leftNode = que.front(); que.pop(); TreeNode* rightNode = que.front(); que.pop(); - if (!leftNode && !rightNode) { // + if (!leftNode && !rightNode) { // 若p的节点与q的节点都为空 continue; } - // + // 若p的节点与q的节点有一个为空或p的节点的值与q节点不同 if ((!leftNode || !rightNode || (leftNode->val != rightNode->val))) { return false; } - que.push(leftNode->left); // - que.push(rightNode->left); // - que.push(leftNode->right); // - que.push(rightNode->right); // + que.push(leftNode->left); // 添加p节点的左子树节点 + que.push(rightNode->left); // 添加q节点的左子树节点 + que.push(leftNode->right); // 添加p节点的右子树节点 + que.push(rightNode->right); // 添加q节点的右子树节点 } return true; } @@ -172,8 +172,72 @@ public: Java: -Python: +```java +// 递归法 +class Solution { + public boolean isSameTree(TreeNode p, TreeNode q) { + if (p == null && q == null) return true; + else if (q == null || p == null) return false; + else if (q.val != p.val) return false; + return isSameTree(q.left, p.left) && isSameTree(q.right, p.right); + } +} +``` +```java +// 迭代法 +class Solution { + public boolean isSameTree(TreeNode p, TreeNode q) { + if(p == null && q == null) return true; + if(p == null || q == null) return false; + Queue que= new LinkedList(); + que.offer(p); + que.offer(q); + while(!que.isEmpty()){ + TreeNode leftNode = que.poll(); + TreeNode rightNode = que.poll(); + if(leftNode == null && rightNode == null) continue; + if(leftNode == null || rightNode== null || leftNode.val != rightNode.val) return false; + que.offer(leftNode.left); + que.offer(rightNode.left); + que.offer(leftNode.right); + que.offer(rightNode.right); + } + return true; + } +} +``` +Python: +```python +# 递归法 +class Solution: + def isSameTree(self, p: TreeNode, q: TreeNode) -> bool: + if not p and not q: return True + elif not p or not q: return False + elif p.val != q.val: return False + return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right) +``` + +```python +# 迭代法 +class Solution: + def isSameTree(self, p: TreeNode, q: TreeNode) -> bool: + if not p and not q: return True + if not p or not q: return False + que = collections.deque() + que.append(p) + que.append(q) + while que: + leftNode = que.popleft() + rightNode = que.popleft() + if not leftNode and not rightNode: continue + if not leftNode or not rightNode or leftNode.val != rightNode.val: return False + que.append(leftNode.left) + que.append(rightNode.left) + que.append(leftNode.right) + que.append(rightNode.right) + return True +``` Go: JavaScript: @@ -182,5 +246,5 @@ JavaScript: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0101.对称二叉树.md b/problems/0101.对称二叉树.md index 241564e9..e091844b 100644 --- a/problems/0101.对称二叉树.md +++ b/problems/0101.对称二叉树.md @@ -73,7 +73,7 @@ bool compare(TreeNode* left, TreeNode* right) 此时左右节点不为空,且数值也不相同的情况我们也处理了。 代码如下: -```C++ +```CPP if (left == NULL && right != NULL) return false; else if (left != NULL && right == NULL) return false; else if (left == NULL && right == NULL) return true; @@ -93,7 +93,7 @@ else if (left->val != right->val) return false; // 注意这里我没有 代码如下: -```C++ +```CPP bool outside = compare(left->left, right->right); // 左子树:左、 右子树:右 bool inside = compare(left->right, right->left); // 左子树:右、 右子树:左 bool isSame = outside && inside; // 左子树:中、 右子树:中(逻辑处理) @@ -104,7 +104,7 @@ return isSame; 最后递归的C++整体代码如下: -```C++ +```CPP class Solution { public: bool compare(TreeNode* left, TreeNode* right) { @@ -137,7 +137,7 @@ public: **盲目的照着抄,结果就是:发现这是一道“简单题”,稀里糊涂的就过了,但是真正的每一步判断逻辑未必想到清楚。** 当然我可以把如上代码整理如下: -```C++ +```CPP class Solution { public: bool compare(TreeNode* left, TreeNode* right) { @@ -177,7 +177,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: bool isSymmetric(TreeNode* root) { @@ -212,7 +212,7 @@ public: 只要把队列原封不动的改成栈就可以了,我下面也给出了代码。 -```C++ +```CPP class Solution { public: bool isSymmetric(TreeNode* root) { @@ -251,6 +251,8 @@ public: # 相关题目推荐 +这两道题目基本和本题是一样的,只要稍加修改就可以AC。 + * 100.相同的树 * 572.另一个树的子树 @@ -579,4 +581,4 @@ var isSymmetric = function(root) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0102.二叉树的层序遍历.md b/problems/0102.二叉树的层序遍历.md index 8be3ac47..0f9b2df6 100644 --- a/problems/0102.二叉树的层序遍历.md +++ b/problems/0102.二叉树的层序遍历.md @@ -6,11 +6,8 @@

欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

-# 二叉树的层序遍历 -看完这篇文章虽然不能打十个,但是可以迅速打八个!而且够快! - -学会二叉树的层序遍历,可以一口气撸完leetcode上八道题目: +学会二叉树的层序遍历,可以一口气打完以下十题: * 102.二叉树的层序遍历 * 107.二叉树的层次遍历II @@ -20,9 +17,16 @@ * 515.在每个树行中找最大值 * 116.填充每个节点的下一个右侧节点指针 * 117.填充每个节点的下一个右侧节点指针II +* 104.二叉树的最大深度 +* 111.二叉树的最小深度 + +在之前写过这篇文章 [二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog),可惜当时只打了5个,还不够,再给我一次机会,我打十个! + +![我要打十个](https://tva1.sinaimg.cn/large/008eGmZEly1gnadnltbpjg309603w4qp.gif) -## 102.二叉树的层序遍历 + +# 102.二叉树的层序遍历 题目地址:https://leetcode-cn.com/problems/binary-tree-level-order-traversal/ @@ -38,7 +42,6 @@ * [二叉树:前中后序迭代法](https://mp.weixin.qq.com/s/OH7aCVJ5-Gi32PkNCoZk4A) * [二叉树:前中后序迭代方式统一写法](https://mp.weixin.qq.com/s/ATQMPCpBlaAgrqdLDMVPZA) - 接下来我们再来介绍二叉树的另一种遍历方式:层序遍历。 层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。这种遍历的方式和我们之前讲过的都不太一样。 @@ -53,11 +56,11 @@ 这样就实现了层序从左到右遍历二叉树。 -代码如下:**这份代码也可以作为二叉树层序遍历的模板,以后再打七个就靠它了**。 +代码如下:**这份代码也可以作为二叉树层序遍历的模板,打十个就靠它了**。 C++代码: -``` +```CPP class Solution { public: vector> levelOrder(TreeNode* root) { @@ -225,9 +228,10 @@ var levelOrder = function(root) { ``` -**此时我们就掌握了二叉树的层序遍历了,那么如下五道leetcode上的题目,只需要修改模板的一两行代码(不能再多了),便可打倒!** +**此时我们就掌握了二叉树的层序遍历了,那么如下九道力扣上的题目,只需要修改模板的两三行代码(不能再多了),便可打倒!** -## 107.二叉树的层次遍历 II + +# 107.二叉树的层次遍历 II 题目链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii/ @@ -241,7 +245,7 @@ var levelOrder = function(root) { C++代码: -```C++ +```CPP class Solution { public: vector> levelOrderBottom(TreeNode* root) { @@ -404,7 +408,7 @@ var levelOrderBottom = function(root) { ``` -## 199.二叉树的右视图 +# 199.二叉树的右视图 题目链接:https://leetcode-cn.com/problems/binary-tree-right-side-view/ @@ -418,7 +422,7 @@ var levelOrderBottom = function(root) { C++代码: -```C++ +```CPP class Solution { public: vector rightSideView(TreeNode* root) { @@ -581,7 +585,7 @@ var rightSideView = function(root) { }; ``` -## 637.二叉树的层平均值 +# 637.二叉树的层平均值 题目链接:https://leetcode-cn.com/problems/average-of-levels-in-binary-tree/ @@ -595,7 +599,7 @@ var rightSideView = function(root) { C++代码: -```C++ +```CPP class Solution { public: vector averageOfLevels(TreeNode* root) { @@ -765,7 +769,7 @@ var averageOfLevels = function(root) { }; ``` -## 429.N叉树的层序遍历 +# 429.N叉树的层序遍历 题目链接:https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/ @@ -790,7 +794,7 @@ var averageOfLevels = function(root) { C++代码: -```C++ +```CPP class Solution { public: vector> levelOrder(Node* root) { @@ -985,7 +989,7 @@ var levelOrder = function(root) { }; ``` -## 515.在每个树行中找最大值 +# 515.在每个树行中找最大值 题目链接:https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/ @@ -999,7 +1003,7 @@ var levelOrder = function(root) { C++代码: -```C++ +```CPP class Solution { public: vector largestValues(TreeNode* root) { @@ -1042,6 +1046,31 @@ class Solution: out_list.append(max(in_list)) return out_list ``` +java代码: + +```java +class Solution { + public List largestValues(TreeNode root) { + List retVal = new ArrayList(); + Queue tmpQueue = new LinkedList(); + if (root != null) tmpQueue.add(root); + + while (tmpQueue.size() != 0){ + int size = tmpQueue.size(); + List lvlVals = new ArrayList(); + for (int index = 0; index < size; index++){ + TreeNode node = tmpQueue.poll(); + lvlVals.add(node.val); + if (node.left != null) tmpQueue.add(node.left); + if (node.right != null) tmpQueue.add(node.right); + } + retVal.add(Collections.max(lvlVals)); + } + + return retVal; + } +} +``` go: @@ -1115,7 +1144,7 @@ var largestValues = function(root) { }; ``` -## 116.填充每个节点的下一个右侧节点指针 +# 116.填充每个节点的下一个右侧节点指针 题目链接:https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/ @@ -1143,7 +1172,7 @@ struct Node { C++代码: -```C++ +```CPP class Solution { public: Node* connect(Node* root) { @@ -1176,6 +1205,35 @@ public: }; ``` +java代码: + +```java +class Solution { + public Node connect(Node root) { + Queue tmpQueue = new LinkedList(); + if (root != null) tmpQueue.add(root); + + while (tmpQueue.size() != 0){ + int size = tmpQueue.size(); + + Node cur = tmpQueue.poll(); + if (cur.left != null) tmpQueue.add(cur.left); + if (cur.right != null) tmpQueue.add(cur.right); + + for (int index = 1; index < size; index++){ + Node next = tmpQueue.poll(); + if (next.left != null) tmpQueue.add(next.left); + if (next.right != null) tmpQueue.add(next.right); + + cur.next = next; + cur = next; + } + } + + return root; + } +} +``` python代码: @@ -1254,7 +1312,8 @@ func connect(root *Node) *Node { } ``` -## 117.填充每个节点的下一个右侧节点指针II +# 117.填充每个节点的下一个右侧节点指针II + 题目地址:https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii/ @@ -1264,7 +1323,7 @@ func connect(root *Node) *Node { C++代码: -```C++ +```CPP class Solution { public: Node* connect(Node* root) { @@ -1295,6 +1354,44 @@ public: } }; ``` + +Java 代码: + +```java +// 二叉树之层次遍历 +class Solution { + public Node connect(Node root) { + Queue queue = new LinkedList<>(); + if (root != null) { + queue.add(root); + } + while (!queue.isEmpty()) { + int size = queue.size(); + Node node = null; + Node nodePre = null; + + for (int i = 0; i < size; i++) { + if (i == 0) { + nodePre = queue.poll(); // 取出本层头一个节点 + node = nodePre; + } else { + node = queue.poll(); + nodePre.next = node; // 本层前一个节点 next 指向当前节点 + nodePre = nodePre.next; + } + if (node.left != null) { + queue.add(node.left); + } + if (node.right != null) { + queue.add(node.right); + } + } + nodePre.next = null; // 本层最后一个节点 next 指向 null + } + return root; + } +} +``` python代码: ```python @@ -1378,12 +1475,119 @@ func connect(root *Node) *Node { return root } ``` +# 104.二叉树的最大深度 -## 总结 +题目地址:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/ -二叉树的层序遍历,就是图论中的广度优先搜索在二叉树中的应用,需要借助队列来实现(此时是不是又发现队列的应用了)。 +给定一个二叉树,找出其最大深度。 -虽然不能一口气打十个,打八个也还行。 +二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 + +说明: 叶子节点是指没有子节点的节点。 + +示例: + +给定二叉树 [3,9,20,null,null,15,7], + +![104. 二叉树的最大深度](https://img-blog.csdnimg.cn/20210203153031914.png) + +返回它的最大深度 3 。 + +思路: + +使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。 + +在二叉树中,一层一层的来遍历二叉树,记录一下遍历的层数就是二叉树的深度,如图所示: + +![层序遍历](https://img-blog.csdnimg.cn/20200810193056585.png) + +所以这道题的迭代法就是一道模板题,可以使用二叉树层序遍历的模板来解决的。 + +C++代码如下: + +```CPP +class Solution { +public: + int maxDepth(TreeNode* root) { + if (root == NULL) return 0; + int depth = 0; + queue que; + que.push(root); + while(!que.empty()) { + int size = que.size(); + depth++; // 记录深度 + for (int i = 0; i < size; i++) { + TreeNode* node = que.front(); + que.pop(); + if (node->left) que.push(node->left); + if (node->right) que.push(node->right); + } + } + return depth; + } +}; +``` + +Java: + + +Python: + +Go: + +JavaScript: + +# 111.二叉树的最小深度 + +相对于 104.二叉树的最大深度 ,本题还也可以使用层序遍历的方式来解决,思路是一样的。 + +**需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点** + +代码如下:(详细注释) + +```CPP +class Solution { +public: + int minDepth(TreeNode* root) { + if (root == NULL) return 0; + int depth = 0; + queue que; + que.push(root); + while(!que.empty()) { + int size = que.size(); + depth++; // 记录最小深度 + for (int i = 0; i < size; i++) { + TreeNode* node = que.front(); + que.pop(); + if (node->left) que.push(node->left); + if (node->right) que.push(node->right); + if (!node->left && !node->right) { // 当左右孩子都为空的时候,说明是最低点的一层了,退出 + return depth; + } + } + } + return depth; + } +}; +``` + +Java: + + +Python: + +Go: + +JavaScript: + + + + +# 总结 + +二叉树的层序遍历,**就是图论中的广度优先搜索在二叉树中的应用**,需要借助队列来实现(此时又发现队列的一个应用了)。 + +来吧,一口气打十个: * 102.二叉树的层序遍历 * 107.二叉树的层次遍历II @@ -1393,289 +1597,15 @@ func connect(root *Node) *Node { * 515.在每个树行中找最大值 * 116.填充每个节点的下一个右侧节点指针 * 117.填充每个节点的下一个右侧节点指针II +* 104.二叉树的最大深度 +* 111.二叉树的最小深度 -如果非要打十个,还得找叶师傅! - -![我要打十个](https://tva1.sinaimg.cn/large/008eGmZEly1gnadnltbpjg309603w4qp.gif) +**致敬叶师傅!** - -# 其他语言版本 - - -> 二叉树的层序遍历(Javascript语言完全版) (迭代 + 递归) - -```js -/** - * 102. 二叉树的层序遍历 - * @param {TreeNode} root - * @return {number[][]} - */ - -// 迭代 - -var levelOrder = function(root) { - const queue = [], res = []; - root && queue.push(root); - while(len = queue.length) { - const val = []; - while(len--) { - const node = queue.shift(); - val.push(node.val); - node.left && queue.push(node.left); - node.right && queue.push(node.right); - } - res.push(val); - } - return res; -}; - -// 递归 -var levelOrder = function(root) { - const res = []; - function defs (root, i) { - if(!root) return; - if(!res[i]) res[i] = []; - res[i].push(root.val) - root.left && defs(root.left, i + 1); - root.right && defs(root.right, i + 1); - } - defs(root, 0); - return res; -}; - - -/** - * 107. 二叉树的层序遍历 II - * @param {TreeNode} root - * @return {number[][]} - */ - -// 迭代 - -var levelOrderBottom = function(root) { - const queue = [], res = []; - root && queue.push(root); - while(len = queue.length) { - const val = []; - while(len--) { - const node = queue.shift(); - val.push(node.val); - node.left && queue.push(node.left); - node.right && queue.push(node.right); - } - res.push(val); - } - return res.reverse() -}; - -// 递归 - -var levelOrderBottom = function(root) { - const res = []; - function defs (root, i) { - if(!root) return; - if(!res[i]) res[i] = []; - res[i].push(root.val); - root.left && defs(root.left, i + 1); - root.right && defs(root.right, i + 1); - } - defs(root, 0); - return res.reverse(); -}; - -/** - * 199. 二叉树的右视图 - * @param {TreeNode} root - * @return {number[]} - */ - -// 迭代 - -var rightSideView = function(root) { - const res = [], queue = []; - root && queue.push(root); - while(l = queue.length) { - while (l--) { - const {val, left, right} = queue.shift(); - !l && res.push(val); - left && queue.push(left); - right && queue.push(right); - } - } - return res; -}; - -// 递归 -var rightSideView = function(root) { - const res = []; - function defs(root, i) { - if(!root) return; - res[i] = root.val; - root.left && defs(root.left, i + 1); - root.right && defs(root.right, i + 1); - } - defs(root, 0); - return res; -}; - -/** - * 637. 二叉树的层平均值 - * @param {TreeNode} root - * @return {number[]} - */ - -// 迭代 -var averageOfLevels = function(root) { - const queue = [], res = []; - root && queue.push(root); - while(len = queue.length) { - let sum = 0, l = len; - while(l--) { - const {val, left, right} = queue.shift(); - sum += val; - left && queue.push(left); - right && queue.push(right); - } - res.push(sum/len); - } - return res; -}; - -// 递归 -var averageOfLevels = function(root) { - const resCount = [], res = []; - function defs(root, i) { - if(!root) return; - if(isNaN(res[i])) resCount[i] = res[i] = 0; - res[i] += root.val; - resCount[i]++; - root.left && defs(root.left, i + 1); - root.right && defs(root.right, i + 1); - } - defs(root, 0); - return res.map((val, i) => val / resCount[i]); -}; - -/** - * 515. 在每个树行中找最大值 - * @param {TreeNode} root - * @return {number[]} - */ - -// 迭代 -const MIN_G = Number.MIN_SAFE_INTEGER; -var largestValues = function(root) { - const queue = [], res = []; - root && queue.push(root); - while(len = queue.length) { - let max = MIN_G; - while(len--) { - const {val, left, right} = queue.shift(); - max = max > val ? max : val; - left && queue.push(left); - right && queue.push(right); - } - res.push(max); - } - return res; -}; - -// 递归 -var largestValues = function(root) { - const res = []; - function defs (root, i) { - if(!root) return; - if(isNaN(res[i])) res[i] = root.val; - res[i] = res[i] > root.val ? res[i] : root.val; - root.left && defs(root.left, i + 1); - root.right && defs(root.right, i + 1); - } - defs(root, 0); - return res; -}; - -/** - * 429. N 叉树的层序遍历 - * @param {Node|null} root - * @return {number[][]} - */ - -// 迭代 -var levelOrder = function(root) { - const queue = [], res = []; - root && queue.push(root); - while(len = queue.length) { - const vals = []; - while(len--) { - const {val, children} = queue.shift(); - vals.push(val); - for(const e of children) { - queue.push(e); - } - } - res.push(vals); - } - return res; -}; - -// 递归 - -var levelOrder = function(root) { - const res = []; - function defs (root, i) { - if(!root) return; - if(!res[i]) res[i] = []; - res[i].push(root.val); - for(const e of root.children) { - defs(e, i + 1); - } - } - defs(root, 0); - return res; -}; - -/** - * 116. 填充每个节点的下一个右侧节点指针 - * 117. 填充每个节点的下一个右侧节点指针 II - * @param {Node} root - * @return {Node} - */ - -// 迭代 -var connect = function(root) { - const queue = []; - root && queue.push(root); - while(len = queue.length) { - while(len--) { - const node1 = queue.shift(), - node2 = len ? queue[0] : null; - node1.next = node2; - node1.left && queue.push(node1.left); - node1.right && queue.push(node1.right); - } - } - return root; -}; - -// 递归 -var connect = function(root) { - const res = []; - function defs (root, i) { - if(!root) return; - if(res[i]) res[i].next = root; - res[i] = root; - root.left && defs(root.left, i + 1); - root.right && defs(root.right, i + 1); - } - defs(root, 0); - return root; -}; -``` - ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0104.二叉树的最大深度.md b/problems/0104.二叉树的最大深度.md index 218a966c..f09361e1 100644 --- a/problems/0104.二叉树的最大深度.md +++ b/problems/0104.二叉树的最大深度.md @@ -8,10 +8,11 @@ 看完本篇可以一起做了如下两道题目: -* 104.二叉树的最大深度 -* 559.N叉树的最大深度 -## 104.二叉树的最大深度 +* 104.二叉树的最大深度 +* 559.n叉树的最大深度 + +# 104.二叉树的最大深度 题目地址:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/ @@ -28,7 +29,7 @@ 返回它的最大深度 3 。 -### 递归法 +## 递归法 本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。 @@ -41,53 +42,53 @@ 1. 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回这棵树的深度,所以返回值为int类型。 代码如下: -``` -int getDepth(TreeNode* node) +```c++ +int getdepth(treenode* node) ``` 2. 确定终止条件:如果为空节点的话,就返回0,表示高度为0。 代码如下: -``` -if (node == NULL) return 0; +```c++ +if (node == null) return 0; ``` 3. 确定单层递归的逻辑:先求它的左子树的深度,再求的右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。 代码如下: -``` -int leftDepth = getDepth(node->left); // 左 -int rightDepth = getDepth(node->right); // 右 -int depth = 1 + max(leftDepth, rightDepth); // 中 +```c++ +int leftdepth = getdepth(node->left); // 左 +int rightdepth = getdepth(node->right); // 右 +int depth = 1 + max(leftdepth, rightdepth); // 中 return depth; ``` -所以整体C++代码如下: +所以整体c++代码如下: -```C++ -class Solution { +```c++ +class solution { public: - int getDepth(TreeNode* node) { - if (node == NULL) return 0; - int leftDepth = getDepth(node->left); // 左 - int rightDepth = getDepth(node->right); // 右 - int depth = 1 + max(leftDepth, rightDepth); // 中 + int getdepth(treenode* node) { + if (node == null) return 0; + int leftdepth = getdepth(node->left); // 左 + int rightdepth = getdepth(node->right); // 右 + int depth = 1 + max(leftdepth, rightdepth); // 中 return depth; } - int maxDepth(TreeNode* root) { - return getDepth(root); + int maxdepth(treenode* root) { + return getdepth(root); } }; ``` -代码精简之后C++代码如下: -```C++ -class Solution { +代码精简之后c++代码如下: +```c++ +class solution { public: - int maxDepth(TreeNode* root) { - if (root == NULL) return 0; - return 1 + max(maxDepth(root->left), maxDepth(root->right)); + int maxdepth(treenode* root) { + if (root == null) return 0; + return 1 + max(maxdepth(root->left), maxdepth(root->right)); } }; @@ -98,31 +99,31 @@ public: 本题当然也可以使用前序,代码如下:(**充分表现出求深度回溯的过程**) -```C++ -class Solution { +```c++ +class solution { public: int result; - void getDepth(TreeNode* node, int depth) { + void getdepth(treenode* node, int depth) { result = depth > result ? depth : result; // 中 - if (node->left == NULL && node->right == NULL) return ; + if (node->left == null && node->right == null) return ; if (node->left) { // 左 depth++; // 深度+1 - getDepth(node->left, depth); + getdepth(node->left, depth); depth--; // 回溯,深度-1 } if (node->right) { // 右 depth++; // 深度+1 - getDepth(node->right, depth); + getdepth(node->right, depth); depth--; // 回溯,深度-1 } return ; } - int maxDepth(TreeNode* root) { + int maxdepth(treenode* root) { result = 0; if (root == 0) return result; - getDepth(root, 1); + getdepth(root, 1); return result; } }; @@ -132,31 +133,31 @@ public: 注意以上代码是为了把细节体现出来,简化一下代码如下: -```C++ -class Solution { +```c++ +class solution { public: int result; - void getDepth(TreeNode* node, int depth) { + void getdepth(treenode* node, int depth) { result = depth > result ? depth : result; // 中 - if (node->left == NULL && node->right == NULL) return ; + if (node->left == null && node->right == null) return ; if (node->left) { // 左 - getDepth(node->left, depth + 1); + getdepth(node->left, depth + 1); } if (node->right) { // 右 - getDepth(node->right, depth + 1); + getdepth(node->right, depth + 1); } return ; } - int maxDepth(TreeNode* root) { + int maxdepth(treenode* root) { result = 0; if (root == 0) return result; - getDepth(root, 1); + getdepth(root, 1); return result; } }; ``` -### 迭代法 +## 迭代法 使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。 @@ -166,23 +167,23 @@ public: 所以这道题的迭代法就是一道模板题,可以使用二叉树层序遍历的模板来解决的。 -如果对层序遍历还不清楚的话,可以看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog) +如果对层序遍历还不清楚的话,可以看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/4-bDKi7SdwfBGRm9FYduiA) -C++代码如下: +c++代码如下: -```C++ -class Solution { +```c++ +class solution { public: - int maxDepth(TreeNode* root) { - if (root == NULL) return 0; + int maxdepth(treenode* root) { + if (root == null) return 0; int depth = 0; - queue que; + queue que; que.push(root); while(!que.empty()) { int size = que.size(); depth++; // 记录深度 for (int i = 0; i < size; i++) { - TreeNode* node = que.front(); + treenode* node = que.front(); que.pop(); if (node->left) que.push(node->left); if (node->right) que.push(node->right); @@ -193,19 +194,19 @@ public: }; ``` -那么我们可以顺便解决一下N叉树的最大深度问题 +那么我们可以顺便解决一下n叉树的最大深度问题 -## 559.N叉树的最大深度 +# 559.n叉树的最大深度 题目地址:https://leetcode-cn.com/problems/maximum-depth-of-n-ary-tree/ -给定一个 N 叉树,找到其最大深度。 +给定一个 n 叉树,找到其最大深度。 最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。 例如,给定一个 3叉树 : -![559.N叉树的最大深度](https://img-blog.csdnimg.cn/2021020315313214.png) +![559.n叉树的最大深度](https://img-blog.csdnimg.cn/2021020315313214.png) 我们应返回其最大深度,3。 @@ -213,39 +214,39 @@ public: 依然可以提供递归法和迭代法,来解决这个问题,思路是和二叉树思路一样的,直接给出代码如下: -### 递归法 +## 递归法 -C++代码: +c++代码: -```C++ -class Solution { +```c++ +class solution { public: - int maxDepth(Node* root) { + int maxdepth(node* root) { if (root == 0) return 0; int depth = 0; for (int i = 0; i < root->children.size(); i++) { - depth = max (depth, maxDepth(root->children[i])); + depth = max (depth, maxdepth(root->children[i])); } return depth + 1; } }; ``` -### 迭代法 +## 迭代法 依然是层序遍历,代码如下: -```C++ -class Solution { +```c++ +class solution { public: - int maxDepth(Node* root) { - queue que; - if (root != NULL) que.push(root); + int maxdepth(node* root) { + queue que; + if (root != null) que.push(root); int depth = 0; while (!que.empty()) { int size = que.size(); depth++; // 记录深度 for (int i = 0; i < size; i++) { - Node* node = que.front(); + node* node = que.front(); que.pop(); for (int j = 0; j < node->children.size(); j++) { if (node->children[j]) que.push(node->children[j]); @@ -257,45 +258,46 @@ public: }; ``` -## 其他语言版本 +# 其他语言版本 +## java -Java: +### 104.二叉树的最大深度 -```Java -class Solution { +```java +class solution { /** * 递归法 */ - public int maxDepth(TreeNode root) { + public int maxdepth(treenode root) { if (root == null) { return 0; } - int leftDepth = maxDepth(root.left); - int rightDepth = maxDepth(root.right); - return Math.max(leftDepth, rightDepth) + 1; + int leftdepth = maxdepth(root.left); + int rightdepth = maxdepth(root.right); + return math.max(leftdepth, rightdepth) + 1; } } ``` -```Java -class Solution { +```java +class solution { /** * 迭代法,使用层序遍历 */ - public int maxDepth(TreeNode root) { + public int maxdepth(treenode root) { if(root == null) { return 0; } - Deque deque = new LinkedList<>(); + deque deque = new linkedlist<>(); deque.offer(root); int depth = 0; - while (!deque.isEmpty()) { + while (!deque.isempty()) { int size = deque.size(); depth++; for (int i = 0; i < size; i++) { - TreeNode poll = deque.poll(); + treenode poll = deque.poll(); if (poll.left != null) { deque.offer(poll.left); } @@ -309,37 +311,39 @@ class Solution { } ``` -Python: +## python -104.二叉树的最大深度 -> 递归法: +### 104.二叉树的最大深度 + +递归法: ```python -class Solution: - def maxDepth(self, root: TreeNode) -> int: - return self.getDepth(root) +class solution: + def maxdepth(self, root: treenode) -> int: + return self.getdepth(root) - def getDepth(self, node): + def getdepth(self, node): if not node: return 0 - leftDepth = self.getDepth(node.left) #左 - rightDepth = self.getDepth(node.right) #右 - depth = 1 + max(leftDepth, rightDepth) #中 + leftdepth = self.getdepth(node.left) #左 + rightdepth = self.getdepth(node.right) #右 + depth = 1 + max(leftdepth, rightdepth) #中 return depth ``` -> 递归法;精简代码 + +递归法:精简代码 ```python -class Solution: - def maxDepth(self, root: TreeNode) -> int: +class solution: + def maxdepth(self, root: treenode) -> int: if not root: return 0 - return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right)) + return 1 + max(self.maxdepth(root.left), self.maxdepth(root.right)) ``` -> 迭代法: +迭代法: ```python import collections -class Solution: - def maxDepth(self, root: TreeNode) -> int: +class solution: + def maxdepth(self, root: treenode) -> int: if not root: return 0 depth = 0 #记录深度 @@ -357,24 +361,25 @@ class Solution: return depth ``` -559.N叉树的最大深度 -> 递归法: +### 559.n叉树的最大深度 + +递归法: ```python -class Solution: - def maxDepth(self, root: 'Node') -> int: +class solution: + def maxdepth(self, root: 'node') -> int: if not root: return 0 depth = 0 for i in range(len(root.children)): - depth = max(depth, self.maxDepth(root.children[i])) + depth = max(depth, self.maxdepth(root.children[i])) return depth + 1 ``` -> 迭代法: +迭代法: ```python import collections -class Solution: - def maxDepth(self, root: 'Node') -> int: +class solution: + def maxdepth(self, root: 'node') -> int: queue = collections.deque() if root: queue.append(root) @@ -390,10 +395,10 @@ class Solution: return depth ``` -> 使用栈来模拟后序遍历依然可以 +使用栈来模拟后序遍历依然可以 ```python -class Solution: - def maxDepth(self, root: 'Node') -> int: +class solution: + def maxdepth(self, root: 'node') -> int: st = [] if root: st.append(root) @@ -401,9 +406,9 @@ class Solution: result = 0 while st: node = st.pop() - if node != None: + if node != none: st.append(node) #中 - st.append(None) + st.append(none) depth += 1 for i in range(len(node.children)): #处理孩子 if node.children[i]: @@ -417,15 +422,15 @@ class Solution: ``` -Go: +## go ```go /** - * Definition for a binary tree node. - * type TreeNode struct { - * Val int - * Left *TreeNode - * Right *TreeNode + * definition for a binary tree node. + * type treenode struct { + * val int + * left *treenode + * right *treenode * } */ func max (a, b int) int { @@ -435,28 +440,28 @@ func max (a, b int) int { return b; } // 递归 -func maxDepth(root *TreeNode) int { +func maxdepth(root *treenode) int { if root == nil { return 0; } - return max(maxDepth(root.Left), maxDepth(root.Right)) + 1; + return max(maxdepth(root.left), maxdepth(root.right)) + 1; } // 遍历 -func maxDepth(root *TreeNode) int { +func maxdepth(root *treenode) int { levl := 0; - queue := make([]*TreeNode, 0); + queue := make([]*treenode, 0); if root != nil { queue = append(queue, root); } for l := len(queue); l > 0; { for ;l > 0;l-- { node := queue[0]; - if node.Left != nil { - queue = append(queue, node.Left); + if node.left != nil { + queue = append(queue, node.left); } - if node.Right != nil { - queue = append(queue, node.Right); + if node.right != nil { + queue = append(queue, node.right); } queue = queue[1:]; } @@ -469,46 +474,49 @@ func maxDepth(root *TreeNode) int { ``` -JavaScript +## javascript + ```javascript -var maxDepth = function(root) { +var maxdepth = function(root) { if (!root) return root - return 1 + Math.max(maxDepth(root.left), maxDepth(root.right)) + return 1 + math.max(maxdepth(root.left), maxdepth(root.right)) }; -``` +``` + 二叉树最大深度递归遍历 ```javascript -var maxDepth = function(root) { +var maxdepth = function(root) { //使用递归的方法 递归三部曲 //1. 确定递归函数的参数和返回值 - const getDepth=function(node){ + const getdepth=function(node){ //2. 确定终止条件 if(node===null){ return 0; } //3. 确定单层逻辑 - let leftDepth=getDepth(node.left); - let rightDepth=getDepth(node.right); - let depth=1+Math.max(leftDepth,rightDepth); + let leftdepth=getdepth(node.left); + let rightdepth=getdepth(node.right); + let depth=1+math.max(leftdepth,rightdepth); return depth; } - return getDepth(root); + return getdepth(root); }; ``` + 二叉树最大深度层级遍历 ```javascript -var maxDepth = function(root) { +var maxdepth = function(root) { //使用递归的方法 递归三部曲 //1. 确定递归函数的参数和返回值 - const getDepth=function(node){ + const getdepth=function(node){ //2. 确定终止条件 if(node===null){ return 0; } //3. 确定单层逻辑 - let leftDepth=getDepth(node.left); - let rightDepth=getDepth(node.right); - let depth=1+Math.max(leftDepth,rightDepth); + let leftdepth=getdepth(node.left); + let rightdepth=getdepth(node.right); + let depth=1+math.max(leftdepth,rightdepth); return depth; } return getDepth(root); @@ -519,4 +527,4 @@ var maxDepth = function(root) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0106.从中序与后序遍历序列构造二叉树.md b/problems/0106.从中序与后序遍历序列构造二叉树.md index 4c5a70a0..4cfe2858 100644 --- a/problems/0106.从中序与后序遍历序列构造二叉树.md +++ b/problems/0106.从中序与后序遍历序列构造二叉树.md @@ -59,7 +59,7 @@ 不难写出如下代码:(先把框架写出来) -```C++ +```CPP TreeNode* traversal (vector& inorder, vector& postorder) { // 第一步 @@ -155,7 +155,7 @@ root->right = traversal(rightInorder, rightPostorder); ### C++完整代码 -```C++ +```CPP class Solution { private: TreeNode* traversal (vector& inorder, vector& postorder) { @@ -209,7 +209,7 @@ public: 加了日志的代码如下:(加了日志的代码不要在leetcode上提交,容易超时) -```C++ +```CPP class Solution { private: TreeNode* traversal (vector& inorder, vector& postorder) { @@ -277,7 +277,7 @@ public: 下面给出用下表索引写出的代码版本:(思路是一样的,只不过不用重复定义vector了,每次用下表索引来分割) ### C++优化版本 -```C++ +```CPP class Solution { private: // 中序区间:[inorderBegin, inorderEnd),后序区间[postorderBegin, postorderEnd) @@ -325,7 +325,7 @@ public: 那么这个版本写出来依然要打日志进行调试,打日志的版本如下:(**该版本不要在leetcode上提交,容易超时**) -```C++ +```CPP class Solution { private: TreeNode* traversal (vector& inorder, int inorderBegin, int inorderEnd, vector& postorder, int postorderBegin, int postorderEnd) { @@ -419,7 +419,7 @@ public: 带日志的版本C++代码如下: (**带日志的版本仅用于调试,不要在leetcode上提交,会超时**) -```C++ +```CPP class Solution { private: TreeNode* traversal (vector& inorder, int inorderBegin, int inorderEnd, vector& preorder, int preorderBegin, int preorderEnd) { @@ -493,7 +493,7 @@ public: 105.从前序与中序遍历序列构造二叉树,最后版本,C++代码: -```C++ +```CPP class Solution { private: TreeNode* traversal (vector& inorder, int inorderBegin, int inorderEnd, vector& preorder, int preorderBegin, int preorderEnd) { @@ -654,43 +654,68 @@ class Solution { ``` Python: + 105.从前序与中序遍历序列构造二叉树 ```python -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right -//递归法 class Solution: def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode: - if not preorder: return None //特殊情况 - root = TreeNode(preorder[0]) //新建父节点 - p=inorder.index(preorder[0]) //找到父节点在中序遍历的位置(因为没有重复的元素,才可以这样找) - root.left = self.buildTree(preorder[1:p+1],inorder[:p]) //注意左节点时分割中序数组和前续数组的开闭环 - root.right = self.buildTree(preorder[p+1:],inorder[p+1:]) //分割中序数组和前续数组 - return root + # 第一步: 特殊情况讨论: 树为空. 或者说是递归终止条件 + if not preorder: + return None + + # 第二步: 前序遍历的第一个就是当前的中间节点. + root_val = preorder[0] + root = TreeNode(root_val) + + # 第三步: 找切割点. + separator_idx = inorder.index(root_val) + + # 第四步: 切割inorder数组. 得到inorder数组的左,右半边. + inorder_left = inorder[:separator_idx] + inorder_right = inorder[separator_idx + 1:] + + # 第五步: 切割preorder数组. 得到preorder数组的左,右半边. + # ⭐️ 重点1: 中序数组大小一定跟前序数组大小是相同的. + preorder_left = preorder[1:1 + len(inorder_left)] + preorder_right = preorder[1 + len(inorder_left):] + + # 第六步: 递归 + root.left = self.buildTree(preorder_left, inorder_left) + root.right = self.buildTree(preorder_right, inorder_right) + + return root ``` 106.从中序与后序遍历序列构造二叉树 ```python -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right -//递归法 class Solution: def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode: - if not postorder: return None //特殊情况 - root = TreeNode(postorder[-1]) //新建父节点 - p=inorder.index(postorder[-1]) //找到父节点在中序遍历的位置*因为没有重复的元素,才可以这样找 - root.left = self.buildTree(inorder[:p],postorder[:p]) //分割中序数组和后续数组 - root.right = self.buildTree(inorder[p+1:],postorder[p:-1]) //注意右节点时分割中序数组和后续数组的开闭环 - return root + # 第一步: 特殊情况讨论: 树为空. (递归终止条件) + if not postorder: + return None + + # 第二步: 后序遍历的最后一个就是当前的中间节点. + root_val = postorder[-1] + root = TreeNode(root_val) + + # 第三步: 找切割点. + separator_idx = inorder.index(root_val) + + # 第四步: 切割inorder数组. 得到inorder数组的左,右半边. + inorder_left = inorder[:separator_idx] + inorder_right = inorder[separator_idx + 1:] + + # 第五步: 切割postorder数组. 得到postorder数组的左,右半边. + # ⭐️ 重点1: 中序数组大小一定跟后序数组大小是相同的. + postorder_left = postorder[:len(inorder_left)] + postorder_right = postorder[len(inorder_left): len(postorder) - 1] + + # 第六步: 递归 + root.left = self.buildTree(inorder_left, postorder_left) + root.right = self.buildTree(inorder_right, postorder_right) + + return root ``` Go: > 106 从中序与后序遍历序列构造二叉树 @@ -793,4 +818,4 @@ var buildTree = function(preorder, inorder) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0108.将有序数组转换为二叉搜索树.md b/problems/0108.将有序数组转换为二叉搜索树.md index 8ec5f3dc..9e67a677 100644 --- a/problems/0108.将有序数组转换为二叉搜索树.md +++ b/problems/0108.将有序数组转换为二叉搜索树.md @@ -122,7 +122,7 @@ return root; * 递归整体代码如下: -```C++ +```CPP class Solution { private: TreeNode* traversal(vector& nums, int left, int right) { @@ -150,7 +150,7 @@ public: 模拟的就是不断分割的过程,C++代码如下:(我已经详细注释) -```C++ +```CPP class Solution { public: TreeNode* sortedArrayToBST(vector& nums) { @@ -388,4 +388,4 @@ var sortedArrayToBST = function (nums) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0110.平衡二叉树.md b/problems/0110.平衡二叉树.md index b6e50853..55d3c2e7 100644 --- a/problems/0110.平衡二叉树.md +++ b/problems/0110.平衡二叉树.md @@ -9,7 +9,7 @@ > 求高度还是求深度,你搞懂了不? -## 110.平衡二叉树 +# 110.平衡二叉树 题目地址:https://leetcode-cn.com/problems/balanced-binary-tree/ @@ -33,9 +33,10 @@ 返回 false 。 -## 题外话 +# 题外话 -咋眼一看这道题目和[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)很像,其实有很大区别。 + +咋眼一看这道题目和[104.二叉树的最大深度](https://mp.weixin.qq.com/s/jRaRcRerhEHepQbt-aKstw)很像,其实有很大区别。 这里强调一波概念: @@ -50,13 +51,13 @@ 因为求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中) -有的同学一定疑惑,为什么[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中求的是二叉树的最大深度,也用的是后序遍历。 +有的同学一定疑惑,为什么[104.二叉树的最大深度](https://mp.weixin.qq.com/s/jRaRcRerhEHepQbt-aKstw)中求的是二叉树的最大深度,也用的是后序遍历。 **那是因为代码的逻辑其实是求的根节点的高度,而根节点的高度就是这颗树的最大深度,所以才可以使用后序遍历。** -在[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中,如果真正求取二叉树的最大深度,代码应该写成如下:(前序遍历) +在[104.二叉树的最大深度](https://mp.weixin.qq.com/s/jRaRcRerhEHepQbt-aKstw)中,如果真正求取二叉树的最大深度,代码应该写成如下:(前序遍历) -```C++ +```CPP class Solution { public: int result; @@ -90,7 +91,7 @@ public: 注意以上代码是为了把细节体现出来,简化一下代码如下: -```C++ +```CPP class Solution { public: int result; @@ -114,9 +115,9 @@ public: }; ``` -## 本题思路 +# 本题思路 -### 递归 +## 递归 此时大家应该明白了既然要求比较高度,必然是要后序遍历。 @@ -160,7 +161,7 @@ if (node == NULL) { 代码如下: -``` +```CPP int leftDepth = depth(node->left); // 左 if (leftDepth == -1) return -1; int rightDepth = depth(node->right); // 右 @@ -178,7 +179,7 @@ return result; 代码精简之后如下: -``` +```CPP int leftDepth = getDepth(node->left); if (leftDepth == -1) return -1; int rightDepth = getDepth(node->right); @@ -190,7 +191,7 @@ return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth); getDepth整体代码如下: -```C++ +```CPP int getDepth(TreeNode* node) { if (node == NULL) { return 0; @@ -205,7 +206,7 @@ int getDepth(TreeNode* node) { 最后本题整体递归代码如下: -```C++ +```CPP class Solution { public: // 返回以该节点为根节点的二叉树的高度,如果不是二叉搜索树了则返回-1 @@ -225,9 +226,9 @@ public: }; ``` -### 迭代 +## 迭代 -在[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中我们可以使用层序遍历来求深度,但是就不能直接用层序遍历来求高度了,这就体现出求高度和求深度的不同。 +在[104.二叉树的最大深度](https://mp.weixin.qq.com/s/jRaRcRerhEHepQbt-aKstw)中我们可以使用层序遍历来求深度,但是就不能直接用层序遍历来求高度了,这就体现出求高度和求深度的不同。 本题的迭代方式可以先定义一个函数,专门用来求高度。 @@ -235,7 +236,7 @@ public: 代码如下: -```C++ +```CPP // cur节点的最大深度,就是cur的高度 int getDepth(TreeNode* cur) { stack st; @@ -266,7 +267,7 @@ int getDepth(TreeNode* cur) { 然后再用栈来模拟前序遍历,遍历每一个节点的时候,再去判断左右孩子的高度是否符合,代码如下: -``` +```CPP bool isBalanced(TreeNode* root) { stack st; if (root == NULL) return true; @@ -286,7 +287,7 @@ bool isBalanced(TreeNode* root) { 整体代码如下: -``` +```CPP class Solution { private: int getDepth(TreeNode* cur) { @@ -342,7 +343,7 @@ public: 因为对于回溯算法已经是非常复杂的递归了,如果在用迭代的话,就是自己给自己找麻烦,效率也并不一定高。 -## 总结 +# 总结 通过本题可以了解求二叉树深度 和 二叉树高度的差异,求深度适合用前序遍历,而求高度适合用后序遍历。 @@ -351,9 +352,9 @@ public: 但是递归方式是一定要掌握的! -## 其他语言版本 +# 其他语言版本 -Java: +## Java ```Java class Solution { @@ -494,9 +495,9 @@ class Solution { } ``` -Python: +## Python -> 递归法: +递归法: ```python class Solution: def isBalanced(self, root: TreeNode) -> bool: @@ -513,7 +514,7 @@ class Solution: return -1 if abs(leftDepth - rightDepth)>1 else 1 + max(leftDepth, rightDepth) ``` -> 迭代法: +迭代法: ```python class Solution: def isBalanced(self, root: TreeNode) -> bool: @@ -553,7 +554,7 @@ class Solution: ``` -Go: +## Go ```Go func isBalanced(root *TreeNode) bool { if root==nil{ @@ -589,7 +590,7 @@ func abs(a int)int{ } ``` -JavaScript: +## JavaScript ```javascript var isBalanced = function(root) { //还是用递归三部曲 + 后序遍历 左右中 当前左子树右子树高度相差大于1就返回-1 @@ -623,4 +624,4 @@ var isBalanced = function(root) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0111.二叉树的最小深度.md b/problems/0111.二叉树的最小深度.md index a36faeff..b4d0e32b 100644 --- a/problems/0111.二叉树的最小深度.md +++ b/problems/0111.二叉树的最小深度.md @@ -9,7 +9,7 @@ > 和求最大深度一个套路? -## 111.二叉树的最小深度 +# 111.二叉树的最小深度 题目地址:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/ @@ -27,9 +27,9 @@ 返回它的最小深度 2. -## 思路 +# 思路 -看完了这篇[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg),再来看看如何求最小深度。 +看完了这篇[104.二叉树的最大深度](https://mp.weixin.qq.com/s/jRaRcRerhEHepQbt-aKstw),再来看看如何求最小深度。 直觉上好像和求最大深度差不多,其实还是差不少的。 @@ -87,7 +87,7 @@ return result; 代码如下: -```C++ +```CPP int leftDepth = getDepth(node->left); // 左 int rightDepth = getDepth(node->right); // 右 // 中 @@ -106,7 +106,7 @@ return result; 遍历的顺序为后序(左右中),可以看出:**求二叉树的最小深度和求二叉树的最大深度的差别主要在于处理左右孩子不为空的逻辑。** 整体递归代码如下: -```C++ +```CPP class Solution { public: int getDepth(TreeNode* node) { @@ -134,7 +134,7 @@ public: 精简之后代码如下: -```C++ +```CPP class Solution { public: int minDepth(TreeNode* root) { @@ -154,15 +154,15 @@ public: ## 迭代法 -相对于[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg),本题还可以使用层序遍历的方式来解决,思路是一样的。 +相对于[104.二叉树的最大深度](https://mp.weixin.qq.com/s/jRaRcRerhEHepQbt-aKstw),本题还可以使用层序遍历的方式来解决,思路是一样的。 -如果对层序遍历还不清楚的话,可以看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog) +如果对层序遍历还不清楚的话,可以看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/4-bDKi7SdwfBGRm9FYduiA) **需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点** 代码如下:(详细注释) -```C++ +```CPP class Solution { public: @@ -190,10 +190,10 @@ public: ``` -## 其他语言版本 +# 其他语言版本 -Java: +## Java ```Java class Solution { @@ -253,7 +253,7 @@ class Solution { } ``` -Python: +## Python 递归法: @@ -299,7 +299,7 @@ class Solution: ``` -Go: +## Go ```go /** @@ -360,7 +360,7 @@ func minDepth(root *TreeNode) int { ``` -JavaScript: +## JavaScript 递归法: @@ -413,4 +413,4 @@ var minDepth = function(root) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0112.路径总和.md b/problems/0112.路径总和.md index ae9a9267..4a814ee1 100644 --- a/problems/0112.路径总和.md +++ b/problems/0112.路径总和.md @@ -9,14 +9,14 @@ > 递归函数什么时候需要返回值 -相信很多同学都会疑惑,递归函数什么时候要有返回值,什么时候没有返回值,特别是有的时候递归函数返回类型为bool类型。那么 +相信很多同学都会疑惑,递归函数什么时候要有返回值,什么时候没有返回值,特别是有的时候递归函数返回类型为bool类型。 -接下来我通过详细讲解如下两道题,来回答这个问题: +那么接下来我通过详细讲解如下两道题,来回答这个问题: * 112.路径总和 -* 113.路径总和II +* 113.路径总和ii -## 112. 路径总和 +# 112. 路径总和 题目地址:https://leetcode-cn.com/problems/path-sum/ @@ -31,11 +31,11 @@ 返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。 -### 思路 +# 思路 这道题我们要遍历从根节点到叶子节点的的路径看看总和是不是目标和。 -### 递归 +## 递归 可以使用深度优先遍历的方式(本题前中后序都可以,无所谓,因为中节点也没有处理逻辑)来遍历二叉树 @@ -43,13 +43,11 @@ 参数:需要二叉树的根节点,还需要一个计数器,这个计数器用来计算二叉树的一条边之和是否正好是目标和,计数器为int型。 -**再来看返回值,递归函数什么时候需要返回值?什么时候不需要返回值?** +再来看返回值,递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点: -在文章[二叉树:我的左下角的值是多少?](https://mp.weixin.qq.com/s/MH2gbLvzQ91jHPKqiub0Nw)中,我给出了一个结论: - -**如果需要搜索整颗二叉树,那么递归函数就不要返回值,如果要搜索其中一条符合条件的路径,递归函数就需要返回值,因为遇到符合条件的路径了就要及时返回。** - -在[二叉树:我的左下角的值是多少?](https://mp.weixin.qq.com/s/MH2gbLvzQ91jHPKqiub0Nw)中,因为要遍历树的所有路径,找出深度最深的叶子节点,所以递归函数不要返回值。 +* 如果需要搜索整颗二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况就是本文下半部分介绍的113.路径总和ii) +* 如果需要搜索整颗二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况我们在[236. 二叉树的最近公共祖先](https://mp.weixin.qq.com/s/n6Rk3nc_X3TSkhXHrVmBTQ)中介绍) +* 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况) 而本题我们要找一条符合条件的路径,所以递归函数需要返回值,及时返回,那么返回类型是什么呢? @@ -62,7 +60,7 @@ 所以代码如下: ``` -bool traversal(TreeNode* cur, int count) // 注意函数的返回类型 +bool traversal(treenode* cur, int count) // 注意函数的返回类型 ``` @@ -91,7 +89,7 @@ if (!cur->left && !cur->right) return false; // 遇到叶子节点而没有找 代码如下: -```C++ +```cpp if (cur->left) { // 左 (空节点不遍历) // 遇到叶子节点返回true,则直接返回true if (traversal(cur->left, count - cur->left->val)) return true; // 注意这里有回溯的逻辑 @@ -109,7 +107,7 @@ return false; 为了把回溯的过程体现出来,可以改为如下代码: -```C++ +```cpp if (cur->left) { // 左 count -= cur->left->val; // 递归,处理节点; if (traversal(cur->left, count)) return true; @@ -126,10 +124,10 @@ return false; 整体代码如下: -```C++ -class Solution { +```cpp +class solution { private: - bool traversal(TreeNode* cur, int count) { + bool traversal(treenode* cur, int count) { if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0 if (!cur->left && !cur->right) return false; // 遇到叶子节点直接返回 @@ -147,8 +145,8 @@ private: } public: - bool hasPathSum(TreeNode* root, int sum) { - if (root == NULL) return false; + bool haspathsum(treenode* root, int sum) { + if (root == null) return false; return traversal(root, sum - root->val); } }; @@ -156,15 +154,15 @@ public: 以上代码精简之后如下: -```C++ -class Solution { +```cpp +class solution { public: - bool hasPathSum(TreeNode* root, int sum) { - if (root == NULL) return false; + bool haspathsum(treenode* root, int sum) { + if (root == null) 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); } }; ``` @@ -172,43 +170,43 @@ public: **是不是发现精简之后的代码,已经完全看不出分析的过程了,所以我们要把题目分析清楚之后,在追求代码精简。** 这一点我已经强调很多次了! -### 迭代 +## 迭代 如果使用栈模拟递归的话,那么如果做回溯呢? **此时栈里一个元素不仅要记录该节点指针,还要记录从头结点到该节点的路径数值总和。** -C++就我们用pair结构来存放这个栈里的元素。 +c++就我们用pair结构来存放这个栈里的元素。 -定义为:`pair` pair<节点指针,路径数值> +定义为:`pair` pair<节点指针,路径数值> 这个为栈里的一个元素。 如下代码是使用栈模拟的前序遍历,如下:(详细注释) -```C++ -class Solution { +```cpp +class solution { public: - bool hasPathSum(TreeNode* root, int sum) { - if (root == NULL) return false; + bool haspathsum(treenode* root, int sum) { + if (root == null) return false; // 此时栈里要放的是pair<节点指针,路径数值> - stack> st; - st.push(pair(root, root->val)); + stack> st; + st.push(pair(root, root->val)); while (!st.empty()) { - pair node = st.top(); + pair node = st.top(); st.pop(); // 如果该节点是叶子节点了,同时该节点的路径数值等于sum,那么就返回true if (!node.first->left && !node.first->right && sum == node.second) return true; // 右节点,压进去一个节点的时候,将该节点的路径数值也记录下来 if (node.first->right) { - st.push(pair(node.first->right, node.second + node.first->right->val)); + st.push(pair(node.first->right, node.second + node.first->right->val)); } // 左节点,压进去一个节点的时候,将该节点的路径数值也记录下来 if (node.first->left) { - st.push(pair(node.first->left, node.second + node.first->left->val)); + st.push(pair(node.first->left, node.second + node.first->left->val)); } } return false; @@ -216,9 +214,9 @@ public: }; ``` -如果大家完全理解了本地的递归方法之后,就可以顺便把leetcode上113. 路径总和II做了。 +如果大家完全理解了本地的递归方法之后,就可以顺便把leetcode上113. 路径总和ii做了。 -## 113. 路径总和II +# 113. 路径总和ii 题目地址:https://leetcode-cn.com/problems/path-sum-ii/ @@ -230,26 +228,27 @@ public: 给定如下二叉树,以及目标和 sum = 22, -![113.路径总和II1.png](https://img-blog.csdnimg.cn/20210203160854654.png) +![113.路径总和ii1.png](https://img-blog.csdnimg.cn/20210203160854654.png) -### 思路 +## 思路 -113.路径总和II要遍历整个树,找到所有路径,**所以递归函数不要返回值!** + +113.路径总和ii要遍历整个树,找到所有路径,**所以递归函数不要返回值!** 如图: -![113.路径总和II](https://img-blog.csdnimg.cn/20210203160922745.png) +![113.路径总和ii](https://img-blog.csdnimg.cn/20210203160922745.png) 为了尽可能的把细节体现出来,我写出如下代码(**这份代码并不简洁,但是逻辑非常清晰**) -```C++ -class Solution { +```cpp +class solution { private: vector> result; vector path; // 递归函数不需要返回值,因为我们要遍历整个树 - void traversal(TreeNode* cur, int count) { + void traversal(treenode* cur, int count) { if (!cur->left && !cur->right && count == 0) { // 遇到了叶子节点且找到了和为sum的路径 result.push_back(path); return; @@ -275,10 +274,10 @@ private: } public: - vector> pathSum(TreeNode* root, int sum) { + vector> pathsum(treenode* root, int sum) { result.clear(); path.clear(); - if (root == NULL) return result; + if (root == null) return result; path.push_back(root->val); // 把根节点放进路径 traversal(root, sum - root->val); return result; @@ -286,11 +285,11 @@ public: }; ``` -至于113. 路径总和II 的迭代法我并没有写,用迭代方式记录所有路径比较麻烦,也没有必要,如果大家感兴趣的话,可以再深入研究研究。 +至于113. 路径总和ii 的迭代法我并没有写,用迭代方式记录所有路径比较麻烦,也没有必要,如果大家感兴趣的话,可以再深入研究研究。 -## 总结 +# 总结 -本篇通过leetcode上112. 路径总和 和 113. 路径总和II 详细的讲解了 递归函数什么时候需要返回值,什么不需要返回值。 +本篇通过leetcode上112. 路径总和 和 113. 路径总和ii 详细的讲解了 递归函数什么时候需要返回值,什么不需要返回值。 这两道题目是掌握这一知识点非常好的题目,大家看完本篇文章再去做题,就会感受到搜索整棵树和搜索某一路径的差别。 @@ -299,31 +298,30 @@ public: +# 其他语言版本 +## java -## 其他语言版本 - - -Java: -```Java -class Solution { - public boolean hasPathSum(TreeNode root, int targetSum) { +lc112 +```java +class solution { + public boolean haspathsum(treenode root, int targetsum) { if (root == null) { return false; } - targetSum -= root.val; + targetsum -= root.val; // 叶子结点 if (root.left == null && root.right == null) { - return targetSum == 0; + return targetsum == 0; } if (root.left != null) { - boolean left = hasPathSum(root.left, targetSum); + boolean left = haspathsum(root.left, targetsum); if (left) {// 已经找到 return true; } } if (root.right != null) { - boolean right = hasPathSum(root.right, targetSum); + boolean right = haspathsum(root.right, targetsum); if (right) {// 已经找到 return true; } @@ -332,34 +330,34 @@ class Solution { } } -// LC112 简洁方法 -class Solution { - public boolean hasPathSum(TreeNode root, int targetSum) { +// lc112 简洁方法 +class solution { + public boolean haspathsum(treenode root, int targetsum) { if (root == null) return false; // 为空退出 // 叶子节点判断是否符合 - if (root.left == null && root.right == null) return root.val == targetSum; + if (root.left == null && root.right == null) return root.val == targetsum; // 求两侧分支的路径和 - return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val); + return haspathsum(root.left, targetsum - root.val) || haspathsum(root.right, targetsum - root.val); } } ``` 迭代 ```java -class Solution { - public boolean hasPathSum(TreeNode root, int targetSum) { +class solution { + public boolean haspathsum(treenode root, int targetsum) { if(root==null)return false; - Stack stack1 = new Stack<>(); - Stack stack2 = new Stack<>(); + stack stack1 = new stack<>(); + stack stack2 = new stack<>(); stack1.push(root);stack2.push(root.val); - while(!stack1.isEmpty()){ + while(!stack1.isempty()){ int size = stack1.size(); for(int i=0;i> pathSum(TreeNode root, int targetSum) { - List> res = new ArrayList<>(); +class solution { + public list> pathsum(treenode root, int targetsum) { + list> res = new arraylist<>(); if (root == null) return res; // 非空判断 - List path = new LinkedList<>(); - preorderDFS(root, targetSum, res, path); + list path = new linkedlist<>(); + preorderdfs(root, targetsum, res, path); return res; } - public void preorderDFS(TreeNode root, int targetSum, List> res, List path) { + public void preorderdfs(treenode root, int targetsum, list> res, list path) { path.add(root.val); // 遇到了叶子节点 if (root.left == null && root.right == null) { - // 找到了和为 targetSum 的路径 - if (targetSum - root.val == 0) { - res.add(new ArrayList<>(path)); + // 找到了和为 targetsum 的路径 + if (targetsum - root.val == 0) { + res.add(new arraylist<>(path)); } - return; // 如果和不为 targetSum,返回 + return; // 如果和不为 targetsum,返回 } if (root.left != null) { - preorderDFS(root.left, targetSum - root.val, res, path); + preorderdfs(root.left, targetsum - root.val, res, path); path.remove(path.size() - 1); // 回溯 } if (root.right != null) { - preorderDFS(root.right, targetSum - root.val, res, path); + preorderdfs(root.right, targetsum - root.val, res, path); path.remove(path.size() - 1); // 回溯 } } } ``` -Python: +## python 0112.路径总和 + +**递归** ```python -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right - -// 递归法 - -class Solution: - def hasPathSum(self, root: TreeNode, targetSum: int) -> bool: - def isornot(root,targetSum)->bool: - if (not root.left) and (not root.right) and targetSum == 0:return True // 遇到叶子节点,并且计数为0 - if (not root.left) and (not root.right):return False //遇到叶子节点,计数不为0 +class solution: + def haspathsum(self, root: treenode, targetsum: int) -> bool: + def isornot(root, targetsum) -> bool: + if (not root.left) and (not root.right) and targetsum == 0: + return true # 遇到叶子节点,并且计数为0 + if (not root.left) and (not root.right): + return false # 遇到叶子节点,计数不为0 if root.left: - targetSum -= root.left.val //左节点 - if isornot(root.left,targetSum):return True //递归,处理左节点 - targetSum += root.left.val //回溯 + targetsum -= root.left.val # 左节点 + if isornot(root.left, targetsum): return true # 递归,处理左节点 + targetsum += root.left.val # 回溯 if root.right: - targetSum -= root.right.val //右节点 - if isornot(root.right,targetSum):return True //递归,处理右节点 - targetSum += root.right.val //回溯 - return False - - if root == None:return False //别忘记处理空TreeNode - else:return isornot(root,targetSum-root.val) + targetsum -= root.right.val # 右节点 + if isornot(root.right, targetsum): return true # 递归,处理右节点 + targetsum += root.right.val # 回溯 + return false + + if root == none: + return false # 别忘记处理空treenode + else: + return isornot(root, targetsum - root.val) +``` + +**迭代 - 层序遍历** +```python +class solution: + def haspathsum(self, root: treenode, targetsum: int) -> bool: + if not root: + return false + + stack = [] # [(当前节点,路径数值), ...] + stack.append((root, root.val)) + + while stack: + cur_node, path_sum = stack.pop() + + if not cur_node.left and not cur_node.right and path_sum == targetsum: + return true + + if cur_node.right: + stack.append((cur_node.right, path_sum + cur_node.right.val)) + + if cur_node.left: + stack.append((cur_node.left, path_sum + cur_node.left.val)) + + return false ``` 0113.路径总和-ii + +**递归** ```python -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right -//递归法 -class Solution: - def pathSum(self, root: TreeNode, targetSum: int) -> List[List[int]]: - path=[] - res=[] - def pathes(root,targetSum): - if (not root.left) and (not root.right) and targetSum == 0: // 遇到叶子节点,并且计数为0 - res.append(path[:]) //找到一种路径,记录到res中,注意必须是path[:]而不是path - return - if (not root.left) and (not root.right):return // 遇到叶子节点直接返回 - if root.left: //左 - targetSum -= root.left.val - path.append(root.left.val) //递归前记录节点 - pathes(root.left,targetSum) //递归 - targetSum += root.left.val //回溯 - path.pop() //回溯 - if root.right: //右 - targetSum -= root.right.val - path.append(root.right.val) //递归前记录节点 - pathes(root.right,targetSum) //递归 - targetSum += root.right.val //回溯 - path.pop() //回溯 - return - - if root == None:return [] //处理空TreeNode - else: - path.append(root.val) //首先处理根节点 - pathes(root,targetSum-root.val) - return res +class solution: + def pathsum(self, root: treenode, targetsum: int) -> list[list[int]]: + + def traversal(cur_node, remain): + if not cur_node.left and not cur_node.right and remain == 0: + result.append(path[:]) + return + + if not cur_node.left and not cur_node.right: return + + if cur_node.left: + path.append(cur_node.left.val) + remain -= cur_node.left.val + traversal(cur_node.left, remain) + path.pop() + remain += cur_node.left.val + + if cur_node.right: + path.append(cur_node.right.val) + remain -= cur_node.right.val + traversal(cur_node.right, remain) + path.pop() + remain += cur_node.right.val + + result, path = [], [] + if not root: + return [] + path.append(root.val) + traversal(root, targetsum - root.val) + return result ``` -Go: +## go -> 112. 路径总和 +112. 路径总和 ```go //递归法 /** - * Definition for a binary tree node. - * type TreeNode struct { - * Val int - * Left *TreeNode - * Right *TreeNode + * definition for a binary tree node. + * type treenode struct { + * val int + * left *treenode + * right *treenode * } */ -func hasPathSum(root *TreeNode, targetSum int) bool { +func haspathsum(root *treenode, targetsum int) bool { var flage bool //找没找到的标志 if root==nil{ return flage } - pathSum(root,0,targetSum,&flage) + pathsum(root,0,targetsum,&flage) return flage } -func pathSum(root *TreeNode, sum int,targetSum int,flage *bool){ - sum+=root.Val - if root.Left==nil&&root.Right==nil&&sum==targetSum{ +func pathsum(root *treenode, sum int,targetsum int,flage *bool){ + sum+=root.val + if root.left==nil&&root.right==nil&&sum==targetsum{ *flage=true return } - if root.Left!=nil&&!(*flage){//左节点不为空且还没找到 - pathSum(root.Left,sum,targetSum,flage) + if root.left!=nil&&!(*flage){//左节点不为空且还没找到 + pathsum(root.left,sum,targetsum,flage) } - if root.Right!=nil&&!(*flage){//右节点不为空且没找到 - pathSum(root.Right,sum,targetSum,flage) + if root.right!=nil&&!(*flage){//右节点不为空且没找到 + pathsum(root.right,sum,targetsum,flage) } } ``` - - -> 113 递归法 +113 递归法 ```go /** - * Definition for a binary tree node. - * type TreeNode struct { - * Val int - * Left *TreeNode - * Right *TreeNode + * definition for a binary tree node. + * type treenode struct { + * val int + * left *treenode + * right *treenode * } */ -func pathSum(root *TreeNode, targetSum int) [][]int { +func pathsum(root *treenode, targetsum int) [][]int { var result [][]int//最终结果 if root==nil{ return result } - var sumNodes []int//经过路径的节点集合 - hasPathSum(root,&sumNodes,targetSum,&result) + var sumnodes []int//经过路径的节点集合 + haspathsum(root,&sumnodes,targetsum,&result) return result } -func hasPathSum(root *TreeNode,sumNodes *[]int,targetSum int,result *[][]int){ - *sumNodes=append(*sumNodes,root.Val) - if root.Left==nil&&root.Right==nil{//叶子节点 - fmt.Println(*sumNodes) +func haspathsum(root *treenode,sumnodes *[]int,targetsum int,result *[][]int){ + *sumnodes=append(*sumnodes,root.val) + if root.left==nil&&root.right==nil{//叶子节点 + fmt.println(*sumnodes) var sum int var number int - for k,v:=range *sumNodes{//求该路径节点的和 + for k,v:=range *sumnodes{//求该路径节点的和 sum+=v number=k } - tempNodes:=make([]int,number+1)//新的nodes接受指针里的值,防止最终指针里的值发生变动,导致最后的结果都是最后一个sumNodes的值 - for k,v:=range *sumNodes{ - tempNodes[k]=v + tempnodes:=make([]int,number+1)//新的nodes接受指针里的值,防止最终指针里的值发生变动,导致最后的结果都是最后一个sumnodes的值 + for k,v:=range *sumnodes{ + tempnodes[k]=v } - if sum==targetSum{ - *result=append(*result,tempNodes) + if sum==targetsum{ + *result=append(*result,tempnodes) } } - if root.Left!=nil{ - hasPathSum(root.Left,sumNodes,targetSum,result) - *sumNodes=(*sumNodes)[:len(*sumNodes)-1]//回溯 + if root.left!=nil{ + haspathsum(root.left,sumnodes,targetsum,result) + *sumnodes=(*sumnodes)[:len(*sumnodes)-1]//回溯 } - if root.Right!=nil{ - hasPathSum(root.Right,sumNodes,targetSum,result) - *sumNodes=(*sumNodes)[:len(*sumNodes)-1]//回溯 + if root.right!=nil{ + haspathsum(root.right,sumnodes,targetsum,result) + *sumnodes=(*sumnodes)[:len(*sumnodes)-1]//回溯 } } ``` -JavaScript: +## javascript 0112.路径总和 ```javascript /** - * @param {TreeNode} root - * @param {number} targetSum + * @param {treenode} root + * @param {number} targetsum * @return {boolean} */ -let hasPathSum = function (root, targetSum) { +let haspathsum = function (root, targetsum) { // 递归法 const traversal = (node, cnt) => { // 遇到叶子节点,并且计数为0 @@ -597,19 +612,19 @@ let hasPathSum = function (root, targetSum) { return false; }; if (!root) return false; - return traversal(root, targetSum - root.val); + return traversal(root, targetsum - root.val); // 精简代码: // if (!root) return false; - // if (!root.left && !root.right && targetSum === root.val) return true; - // return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val); + // if (!root.left && !root.right && targetsum === root.val) return true; + // return haspathsum(root.left, targetsum - root.val) || haspathsum(root.right, targetsum - root.val); }; ``` 0113.路径总和-ii ```javascript -let pathSum = function (root, targetSum) { +let pathsum = function (root, targetsum) { // 递归法 // 要遍历整个树找到所有路径,所以递归函数不需要返回值, 与112不同 const res = []; @@ -635,62 +650,29 @@ let pathSum = function (root, targetSum) { return; }; if (!root) return res; - travelsal(root, targetSum - root.val, [root.val]); // 把根节点放进路径 + travelsal(root, targetsum - root.val, [root.val]); // 把根节点放进路径 return res; }; ``` - -0112 路径总和 +113 路径总和 精简版 ```javascript -var hasPathSum = function(root, targetSum) { +var pathsum = function(root, targetsum) { //递归方法 - // 1. 确定函数参数 - const traversal = function(node,count){ - // 2. 确定终止条件 - if(node.left===null&&node.right===null&&count===0){ - return true; - } - if(node.left===null&&node.right===null){ - return false; - } - //3. 单层递归逻辑 - if(node.left){ - if(traversal(node.left,count-node.left.val)){ - return true; - } - } - if(node.right){ - if(traversal(node.right,count-node.right.val)){ - return true; - } - } - return false; - } - if(root===null){ - return false; - } - return traversal(root,targetSum-root.val); -}; -``` -113 路径总和 -```javascript -var pathSum = function(root, targetSum) { - //递归方法 - let resPath = [],curPath = []; + let respath = [],curpath = []; // 1. 确定递归函数参数 - const travelTree = function(node,count){ - curPath.push(node.val); + const traveltree = function(node,count){ + curpath.push(node.val); count-=node.val; if(node.left===null&&node.right===null&&count===0){ - resPath.push([...curPath]); + respath.push([...curpath]); } - node.left&&travelTree(node.left,count); - node.right&&travelTree(node.right,count); - let cur = curPath.pop(); + node.left&&traveltree(node.left,count); + node.right&&traveltree(node.right,count); + let cur = curpath.pop(); count-=cur; } if(root===null){ - return resPath; + return respath; } travelTree(root,targetSum); return resPath; @@ -699,8 +681,9 @@ var pathSum = function(root, targetSum) { + ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0115.不同的子序列.md b/problems/0115.不同的子序列.md index 014eb3cd..e964e95d 100644 --- a/problems/0115.不同的子序列.md +++ b/problems/0115.不同的子序列.md @@ -82,7 +82,7 @@ dp[0][0]应该是1,空字符串s,可以删除0个元素,变成空字符串 初始化分析完毕,代码如下: -```C++ +```CPP vector> dp(s.size() + 1, vector(t.size() + 1)); for (int i = 0; i <= s.size(); i++) dp[i][0] = 1; for (int j = 1; j <= t.size(); j++) dp[0][j] = 0; // 其实这行代码可以和dp数组初始化的时候放在一起,但我为了凸显初始化的逻辑,所以还是加上了。 @@ -97,7 +97,7 @@ for (int j = 1; j <= t.size(); j++) dp[0][j] = 0; // 其实这行代码可以和 代码如下: -```C++ +```CPP for (int i = 1; i <= s.size(); i++) { for (int j = 1; j <= t.size(); j++) { if (s[i - 1] == t[j - 1]) { @@ -120,7 +120,7 @@ for (int i = 1; i <= s.size(); i++) { 动规五部曲分析完毕,代码如下: -```C++ +```CPP class Solution { public: int numDistinct(string s, string t) { @@ -250,4 +250,4 @@ const numDistinct = (s, t) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0116.填充每个节点的下一个右侧节点指针.md b/problems/0116.填充每个节点的下一个右侧节点指针.md index 28e6f645..34c666f3 100644 --- a/problems/0116.填充每个节点的下一个右侧节点指针.md +++ b/problems/0116.填充每个节点的下一个右侧节点指针.md @@ -52,7 +52,7 @@ struct Node { 图中cur节点为元素4,那么搭线的逻辑代码:(**注意注释中操作1和操作2和图中的对应关系**) -```C++ +```CPP if (cur->left) cur->left->next = cur->right; // 操作1 if (cur->right) { if (cur->next) cur->right->next = cur->next->left; // 操作2 @@ -63,7 +63,7 @@ if (cur->right) { 理解到这里,使用前序遍历,那么不难写出如下代码: -```C++ +```CPP class Solution { private: void traversal(Node* cur) { @@ -93,7 +93,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: Node* connect(Node* root) { @@ -130,26 +130,134 @@ public: ## Java ```java +// 递归法 +class Solution { + public void traversal(Node cur) { + if (cur == null) return; + if (cur.left != null) cur.left.next = cur.right; // 操作1 + if (cur.right != null) { + if(cur.next != null) cur.right.next = cur.next.left; //操作2 + else cur.right.next = null; + } + traversal(cur.left); // 左 + traversal(cur.right); //右 + } + public Node connect(Node root) { + traversal(root); + return root; + } +} +``` +```java +// 迭代法 +class Solution { + public Node connect(Node root) { + if (root == null) return root; + Queue que = new LinkedList(); + que.offer(root); + Node nodePre = null; + Node node = null; + while (!que.isEmpty()) { + int size = que.size(); + for (int i=0; i 'Node': + def traversal(cur: 'Node') -> 'Node': + if not cur: return [] + if cur.left: cur.left.next = cur.right # 操作1 + if cur.right: + if cur.next: + cur.right.next = cur.next.left # 操作2 + else: + cur.right.next = None + traversal(cur.left) # 左 + traversal(cur.right) # 右 + traversal(root) + return root +``` +```python +# 迭代法 +class Solution: + def connect(self, root: 'Node') -> 'Node': + if not root: return + res = [] + queue = [root] + while queue: + size = len(queue) + for i in range(size): # 开始每一层的遍历 + if i==0: + nodePre = queue.pop(0) # 记录一层的头结点 + node = nodePre + else: + node = queue.pop(0) + nodePre.next = node # 本层前一个节点next指向本节点 + nodePre = nodePre.next + if node.left: queue.append(node.left) + if node.right: queue.append(node.right) + nodePre.next = None # 本层最后一个节点指向None + return root ``` - ## Go ```go + ``` ## JavaScript ```js +const connect = root => { + if (!root) return root; + // 根节点入队 + const Q = [root]; + while (Q.length) { + const len = Q.length; + // 遍历这一层的所有节点 + for (let i = 0; i < len; i++) { + // 队头出队 + const node = Q.shift(); + // 连接 + if (i < len - 1) { + // 新的队头是node的右边元素 + node.next = Q[0]; + } + // 队头左节点有值,放入队列 + node.left && Q.push(node.left); + // 队头右节点有值,放入队列 + node.right && Q.push(node.right); + } + } + return root; +}; ``` ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0121.买卖股票的最佳时机.md b/problems/0121.买卖股票的最佳时机.md index 259fff34..6d068c22 100644 --- a/problems/0121.买卖股票的最佳时机.md +++ b/problems/0121.买卖股票的最佳时机.md @@ -33,7 +33,7 @@ 这道题目最直观的想法,就是暴力,找最优间距了。 -```C++ +```CPP class Solution { public: int maxProfit(vector& prices) { @@ -59,7 +59,7 @@ public: C++代码如下: -```C++ +```CPP class Solution { public: int maxProfit(vector& prices) { @@ -139,7 +139,7 @@ dp[5][1]就是最终结果。 以上分析完毕,C++代码如下: -```C++ +```CPP // 版本一 class Solution { public: @@ -169,7 +169,7 @@ dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]); 那么我们只需要记录 当前天的dp状态和前一天的dp状态就可以了,可以使用滚动数组来节省空间,代码如下: -```C++ +```CPP // 版本二 class Solution { public: @@ -313,6 +313,26 @@ func max(a,b int)int { } ``` +JavaScript: + +```javascript +const maxProfit = prices => { + const len = prices.length; + // 创建dp数组 + const dp = new Array(len).fill([0, 0]); + // dp数组初始化 + dp[0] = [-prices[0], 0]; + for (let i = 1; i < len; i++) { + // 更新dp[i] + dp[i] = [ + Math.max(dp[i - 1][0], -prices[i]), + Math.max(dp[i - 1][1], prices[i] + dp[i - 1][0]), + ]; + } + return dp[len - 1][1]; +}; +``` + @@ -320,4 +340,4 @@ func max(a,b int)int { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0122.买卖股票的最佳时机II.md b/problems/0122.买卖股票的最佳时机II.md index 60d4591f..53737532 100644 --- a/problems/0122.买卖股票的最佳时机II.md +++ b/problems/0122.买卖股票的最佳时机II.md @@ -80,7 +80,7 @@ 对应C++代码如下: -```C++ +```CPP class Solution { public: int maxProfit(vector& prices) { @@ -99,7 +99,7 @@ public: 动态规划将在下一个系列详细讲解,本题解先给出我的C++代码(带详细注释),感兴趣的同学可以自己先学习一下。 -```C++ +```CPP class Solution { public: int maxProfit(vector& prices) { @@ -239,4 +239,4 @@ var maxProfit = function(prices) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0122.买卖股票的最佳时机II(动态规划).md b/problems/0122.买卖股票的最佳时机II(动态规划).md index 8ed70063..7492ee04 100644 --- a/problems/0122.买卖股票的最佳时机II(动态规划).md +++ b/problems/0122.买卖股票的最佳时机II(动态规划).md @@ -74,7 +74,7 @@ 代码如下:(注意代码中的注释,标记了和121.买卖股票的最佳时机唯一不同的地方) -```C++ +```CPP class Solution { public: int maxProfit(vector& prices) { @@ -106,7 +106,7 @@ dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); 这里我依然给出滚动数组的版本,C++代码如下: -```C++ +```CPP // 版本二 class Solution { public: @@ -231,4 +231,4 @@ const maxProfit = (prices) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0123.买卖股票的最佳时机III.md b/problems/0123.买卖股票的最佳时机III.md index 7ff1bfe2..0c92a5a2 100644 --- a/problems/0123.买卖股票的最佳时机III.md +++ b/problems/0123.买卖股票的最佳时机III.md @@ -127,7 +127,7 @@ dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]); 以上五部都分析完了,不难写出如下代码: -```C++ +```CPP // 版本一 class Solution { public: @@ -153,7 +153,7 @@ public: 当然,大家可以看到力扣官方题解里的一种优化空间写法,我这里给出对应的C++版本: -```C++ +```CPP // 版本二 class Solution { public: @@ -278,7 +278,44 @@ class Solution: return dp[4] ``` -Go: +JavaScript: + +> 版本一: + +```javascript +const maxProfit = prices => { + const len = prices.length; + const dp = new Array(len).fill(0).map(x => new Array(5).fill(0)); + dp[0][1] = -prices[0]; + dp[0][3] = -prices[0]; + for (let i = 1; i < len; i++) { + dp[i][0] = dp[i - 1][0]; + dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]); + dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]); + dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]); + dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]); + } + return dp[len - 1][4]; +}; +``` + +> 版本二: + +```javascript +const maxProfit = prices => { + const len = prices.length; + const dp = new Array(5).fill(0); + dp[1] = -prices[0]; + dp[3] = -prices[0]; + for (let i = 1; i < len; i++) { + dp[1] = Math.max(dp[1], dp[0] - prices[i]); + dp[2] = Math.max(dp[2], dp[1] + prices[i]); + dp[3] = Math.max(dp[3], dp[2] - prices[i]); + dp[4] = Math.max(dp[4], dp[3] + prices[i]); + } + return dp[4]; +}; +``` @@ -287,4 +324,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0129.求根到叶子节点数字之和.md b/problems/0129.求根到叶子节点数字之和.md index f8c93382..17642793 100644 --- a/problems/0129.求根到叶子节点数字之和.md +++ b/problems/0129.求根到叶子节点数字之和.md @@ -52,7 +52,7 @@ if (!cur->left && !cur->right) { // 遇到了叶子节点 这里vectorToInt函数就是把数组转成int,代码如下: -```C++ +```CPP int vectorToInt(const vector& vec) { int sum = 0; for (int i = 0; i < vec.size(); i++) { @@ -78,7 +78,7 @@ int vectorToInt(const vector& vec) { 代码如下: -```C++ +```CPP // 中 if (cur->left) { // 左 (空节点不遍历) path.push_back(cur->left->val); @@ -94,7 +94,7 @@ if (cur->right) { // 右 (空节点不遍历) 这里要注意回溯和递归要永远在一起,一个递归,对应一个回溯,是一对一的关系,有的同学写成如下代码: -```C++ +```CPP if (cur->left) { // 左 (空节点不遍历) path.push_back(cur->left->val); traversal(cur->left); // 递归 @@ -111,7 +111,7 @@ path.pop_back(); // 回溯 关键逻辑分析完了,整体C++代码如下: -```C++ +```CPP class Solution { private: int result; @@ -165,7 +165,32 @@ public: Java: Python: +```python3 +class Solution: + def sumNumbers(self, root: TreeNode) -> int: + res = 0 + path = [] + def backtrace(root): + nonlocal res + if not root: return # 节点空则返回 + path.append(root.val) + if not root.left and not root.right: # 遇到了叶子节点 + res += get_sum(path) + if root.left: # 左子树不空 + backtrace(root.left) + if root.right: # 右子树不空 + backtrace(root.right) + path.pop() + def get_sum(arr): + s = 0 + for i in range(len(arr)): + s = s * 10 + arr[i] + return s + + backtrace(root) + return res +``` Go: JavaScript: @@ -176,4 +201,4 @@ JavaScript: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0131.分割回文串.md b/problems/0131.分割回文串.md index 43453409..6fe2758b 100644 --- a/problems/0131.分割回文串.md +++ b/problems/0131.分割回文串.md @@ -70,7 +70,7 @@ 代码如下: -```C++ +```CPP vector> result; vector path; // 放已经回文的子串 void backtracking (const string& s, int startIndex) { @@ -88,7 +88,7 @@ void backtracking (const string& s, int startIndex) { 所以终止条件代码如下: -```C++ +```CPP void backtracking (const string& s, int startIndex) { // 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了 if (startIndex >= s.size()) { @@ -108,7 +108,7 @@ void backtracking (const string& s, int startIndex) { 代码如下: -```C++ +```CPP for (int i = startIndex; i < s.size(); i++) { if (isPalindrome(s, startIndex, i)) { // 是回文子串 // 获取[startIndex,i]在s中的子串 @@ -132,7 +132,7 @@ for (int i = startIndex; i < s.size(); i++) { 那么判断回文的C++代码如下: -```C++ +```CPP bool isPalindrome(const string& s, int start, int end) { for (int i = start, j = end; i < j; i++, j--) { if (s[i] != s[j]) { @@ -151,7 +151,7 @@ for (int i = startIndex; i < s.size(); i++) { 根据Carl给出的回溯算法模板: -```C++ +```CPP void backtracking(参数) { if (终止条件) { 存放结果; @@ -169,7 +169,7 @@ void backtracking(参数) { 不难写出如下代码: -```C++ +```CPP class Solution { private: vector> result; @@ -395,4 +395,4 @@ var partition = function(s) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0132.分割回文串II.md b/problems/0132.分割回文串II.md index 856262d6..80784301 100644 --- a/problems/0132.分割回文串II.md +++ b/problems/0132.分割回文串II.md @@ -101,7 +101,7 @@ dp[i]: 范围是[0, i]的回文子串,最少分割次数是dp[i]。 代码如下: -```C++ +```CPP vector dp(s.size(), INT_MAX); dp[0] = 0; ``` @@ -109,7 +109,7 @@ dp[0] = 0; 其实也可以这样初始化,更具dp[i]的定义,dp[i]的最大值其实就是i,也就是把每个字符分割出来。 所以初始化代码也可以为: -```C++ +```CPP vector dp(s.size()); for (int i = 0; i < s.size(); i++) dp[i] = i; ``` @@ -122,7 +122,7 @@ j是在[0,i]之间,所以遍历i的for循环一定在外层,这里遍历j 代码如下: -```C++ +```CPP for (int i = 1; i < s.size(); i++) { if (isPalindromic[0][i]) { // 判断是不是回文子串 dp[i] = 0; @@ -149,7 +149,7 @@ for (int i = 1; i < s.size(); i++) { 代码如下: -```C++ +```CPP vector> isPalindromic(s.size(), vector(s.size(), false)); for (int i = s.size() - 1; i >= 0; i--) { for (int j = i; j < s.size(); j++) { @@ -168,7 +168,7 @@ for (int i = s.size() - 1; i >= 0; i--) { 以上分析完毕,代码如下: -```C++ +```CPP class Solution { public: int minCut(string s) { @@ -252,5 +252,5 @@ class Solution: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0134.加油站.md b/problems/0134.加油站.md index 9b660ea0..e270c059 100644 --- a/problems/0134.加油站.md +++ b/problems/0134.加油站.md @@ -65,7 +65,7 @@ cost = [3,4,3] C++代码如下: -```C++ +```CPP class Solution { public: int canCompleteCircuit(vector& gas, vector& cost) { @@ -99,7 +99,7 @@ C++暴力解法在leetcode上提交也可以过。 C++代码如下: -```C++ +```CPP class Solution { public: int canCompleteCircuit(vector& gas, vector& cost) { @@ -160,7 +160,7 @@ i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i C++代码如下: -```C++ +```CPP class Solution { public: int canCompleteCircuit(vector& gas, vector& cost) { @@ -288,4 +288,4 @@ var canCompleteCircuit = function(gas, cost) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0135.分发糖果.md b/problems/0135.分发糖果.md index 2d3fca84..3865e14b 100644 --- a/problems/0135.分发糖果.md +++ b/problems/0135.分发糖果.md @@ -47,7 +47,7 @@ 代码如下: -```C++ +```CPP // 从前向后 for (int i = 1; i < ratings.size(); i++) { if (ratings[i] > ratings[i - 1]) candyVec[i] = candyVec[i - 1] + 1; @@ -80,7 +80,7 @@ for (int i = 1; i < ratings.size(); i++) { 所以该过程代码如下: -```C++ +```CPP // 从后向前 for (int i = ratings.size() - 2; i >= 0; i--) { if (ratings[i] > ratings[i + 1] ) { @@ -90,7 +90,7 @@ for (int i = ratings.size() - 2; i >= 0; i--) { ``` 整体代码如下: -```C++ +```CPP class Solution { public: int candy(vector& ratings) { @@ -242,4 +242,4 @@ var candy = function(ratings) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0139.单词拆分.md b/problems/0139.单词拆分.md index 4ba08aa9..0c19a614 100644 --- a/problems/0139.单词拆分.md +++ b/problems/0139.单词拆分.md @@ -45,7 +45,7 @@ 那么这里我也给出回溯法C++代码: -```C++ +```CPP class Solution { private: bool backtracking (const string& s, const unordered_set& wordSet, int startIndex) { @@ -86,7 +86,7 @@ public: C++代码如下: -```C++ +```CPP class Solution { private: bool backtracking (const string& s, @@ -190,7 +190,7 @@ dp[s.size()]就是最终结果。 动规五部曲分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: bool wordBreak(string s, vector& wordDict) { @@ -319,4 +319,4 @@ const wordBreak = (s, wordDict) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0141.环形链表.md b/problems/0141.环形链表.md index 4a40f953..78bcfd43 100644 --- a/problems/0141.环形链表.md +++ b/problems/0141.环形链表.md @@ -14,7 +14,7 @@ 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。 如果链表中存在环,则返回 true 。 否则,返回 false 。 -  + ![](https://code-thinking-1253855093.file.myqcloud.com/pics/20210727173600.png) # 思路 @@ -45,7 +45,7 @@ fast和slow各自再走一步, fast和slow就相遇了 C++代码如下 -```C++ +```CPP class Solution { public: bool hasCycle(ListNode *head) { @@ -74,6 +74,21 @@ public: ## Java ```java +public class Solution { + public boolean hasCycle(ListNode head) { + ListNode fast = head; + ListNode slow = head; + // 空链表、单节点链表一定不会有环 + while (fast != null && fast.next != null) { + fast = fast.next.next; // 快指针,一次移动两步 + slow = slow.next; // 慢指针,一次移动一步 + if (fast == slow) { // 快慢指针相遇,表明有环 + return true; + } + } + return false; // 正常走到链表末尾,表明没有环 + } +} ``` ## Python @@ -105,5 +120,5 @@ class Solution: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0142.环形链表II.md b/problems/0142.环形链表II.md index 9deb1e0c..91e14e27 100644 --- a/problems/0142.环形链表II.md +++ b/problems/0142.环形链表II.md @@ -109,7 +109,7 @@ fast指针走过的节点数:` x + y + n (y + z)`,n为fast指针在环内走 代码如下: -```C++ +```CPP /** * Definition for singly-linked list. * struct ListNode { @@ -301,4 +301,4 @@ var detectCycle = function(head) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0143.重排链表.md b/problems/0143.重排链表.md index c4e8d8f7..76df63b7 100644 --- a/problems/0143.重排链表.md +++ b/problems/0143.重排链表.md @@ -1,4 +1,3 @@ -

@@ -25,7 +24,7 @@ 代码如下: -```C++ +```CPP class Solution { public: void reorderList(ListNode* head) { @@ -63,7 +62,8 @@ public: ## 方法二 把链表放进双向队列,然后通过双向队列一前一后弹出数据,来构造新的链表。这种方法比操作数组容易一些,不用双指针模拟一前一后了 -```C++ + +```CPP class Solution { public: void reorderList(ListNode* head) { @@ -108,7 +108,7 @@ public: 代码如下: -```C++ +```CPP class Solution { private: // 反转链表 @@ -176,15 +176,116 @@ public: Java: -Python: +```java +public class ReorderList { + public void reorderList(ListNode head) { + ListNode fast = head, slow = head; + //求出中点 + while (fast.next != null && fast.next.next != null) { + slow = slow.next; + fast = fast.next.next; + } + //right就是右半部分 12345 就是45 1234 就是34 + ListNode right = slow.next; + //断开左部分和右部分 + slow.next = null; + //反转右部分 right就是反转后右部分的起点 + right = reverseList(right); + //左部分的起点 + ListNode left = head; + //进行左右部分来回连接 + //这里左部分的节点个数一定大于等于右部分的节点个数 因此只判断right即可 + while (right != null) { + ListNode curLeft = left.next; + left.next = right; + left = curLeft; + ListNode curRight = right.next; + right.next = left; + right = curRight; + } + } + + public ListNode reverseList(ListNode head) { + ListNode headNode = new ListNode(0); + ListNode cur = head; + ListNode next = null; + while (cur != null) { + next = cur.next; + cur.next = headNode.next; + headNode.next = cur; + cur = next; + } + return headNode.next; + } +} +``` + +Python: +```python3 +# 方法二 双向队列 +class Solution: + def reorderList(self, head: ListNode) -> None: + """ + Do not return anything, modify head in-place instead. + """ + d = collections.deque() + tmp = head + while tmp.next: # 链表除了首元素全部加入双向队列 + d.append(tmp.next) + tmp = tmp.next + tmp = head + while len(d): # 一后一前加入链表 + tmp.next = d.pop() + tmp = tmp.next + if len(d): + tmp.next = d.popleft() + tmp = tmp.next + tmp.next = None # 尾部置空 + +# 方法三 反转链表 +class Solution: + def reorderList(self, head: ListNode) -> None: + if head == None or head.next == None: + return True + slow, fast = head, head + while fast and fast.next: + slow = slow.next + fast = fast.next.next + right = slow.next # 分割右半边 + slow.next = None # 切断 + right = self.reverseList(right) #反转右半边 + left = head + # 左半边一定比右半边长, 因此判断右半边即可 + while right: + curLeft = left.next + left.next = right + left = curLeft + + curRight = right.next + right.next = left + right = curRight + + + def reverseList(self, head: ListNode) -> ListNode: + cur = head + pre = None + while(cur!=None): + temp = cur.next # 保存一下cur的下一个节点 + cur.next = pre # 反转 + pre = cur + cur = temp + return pre +``` Go: JavaScript: ----------------------- + * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -

+ +
diff --git a/problems/0150.逆波兰表达式求值.md b/problems/0150.逆波兰表达式求值.md index aceb91b4..2b294337 100644 --- a/problems/0150.逆波兰表达式求值.md +++ b/problems/0150.逆波兰表达式求值.md @@ -80,7 +80,7 @@ https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/ C++代码如下: -```C++ +```CPP class Solution { public: int evalRPN(vector& tokens) { @@ -241,4 +241,4 @@ def evalRPN(tokens) -> int: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0151.翻转字符串里的单词.md b/problems/0151.翻转字符串里的单词.md index 32d9deef..3f4d00b2 100644 --- a/problems/0151.翻转字符串里的单词.md +++ b/problems/0151.翻转字符串里的单词.md @@ -61,7 +61,7 @@ https://leetcode-cn.com/problems/reverse-words-in-a-string/ 思路很明确了,我们说一说代码的实现细节,就拿移除多余空格来说,一些同学会上来写如下代码: -```C++ +```CPP void removeExtraSpaces(string& s) { for (int i = s.size() - 1; i > 0; i--) { if (s[i] == s[i - 1] && s[i] == ' ') { @@ -93,7 +93,7 @@ erase操作上面还套了一个for循环,那么以上代码移除冗余空格 那么使用双指针来移除冗余空格代码如下: fastIndex走的快,slowIndex走的慢,最后slowIndex就标记着移除多余空格后新字符串的长度。 -```C++ +```CPP void removeExtraSpaces(string& s) { int slowIndex = 0, fastIndex = 0; // 定义快指针,慢指针 // 去掉字符串前面的空格 @@ -141,7 +141,7 @@ void reverse(string& s, int start, int end) { 本题C++整体代码 -```C++ +```CPP // 版本一 class Solution { public: @@ -474,4 +474,4 @@ function reverse(strArr, start, end) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0160.相交链表.md b/problems/0160.相交链表.md index d26f66fd..7ac1848b 100644 --- a/problems/0160.相交链表.md +++ b/problems/0160.相交链表.md @@ -1,2 +1,3 @@ 同:[链表:链表相交](./面试题02.07.链表相交.md) +
diff --git a/problems/0188.买卖股票的最佳时机IV.md b/problems/0188.买卖股票的最佳时机IV.md index 46c6f7f0..9fe7a919 100644 --- a/problems/0188.买卖股票的最佳时机IV.md +++ b/problems/0188.买卖股票的最佳时机IV.md @@ -84,7 +84,7 @@ vector> dp(prices.size(), vector(2 * k + 1, 0)); 同理可以类比剩下的状态,代码如下: -```C++ +```CPP for (int j = 0; j < 2 * k - 1; j += 2) { dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]); dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]); @@ -117,7 +117,7 @@ for (int j = 0; j < 2 * k - 1; j += 2) { 代码如下: -```C++ +```CPP for (int j = 1; j < 2 * k; j += 2) { dp[0][j] = -prices[0]; } @@ -139,7 +139,7 @@ for (int j = 1; j < 2 * k; j += 2) { 以上分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int maxProfit(int k, vector& prices) { @@ -287,4 +287,4 @@ const maxProfit = (k,prices) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0189.旋转数组.md b/problems/0189.旋转数组.md index 9f565c1d..be46bf4c 100644 --- a/problems/0189.旋转数组.md +++ b/problems/0189.旋转数组.md @@ -69,7 +69,7 @@ C++代码如下: -```C++ +```CPP class Solution { public: void rotate(vector& nums, int k) { @@ -137,6 +137,6 @@ class Solution: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0198.打家劫舍.md b/problems/0198.打家劫舍.md index 63a68c36..93b56dae 100644 --- a/problems/0198.打家劫舍.md +++ b/problems/0198.打家劫舍.md @@ -25,7 +25,7 @@ 输出:12 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。   偷窃到的最高金额 = 2 + 9 + 1 = 12 。 -  + 提示: @@ -59,7 +59,7 @@ 代码如下: -```C++ +```CPP vector dp(nums.size()); dp[0] = nums[0]; dp[1] = max(nums[0], nums[1]); @@ -70,7 +70,7 @@ dp[1] = max(nums[0], nums[1]); dp[i] 是根据dp[i - 2] 和 dp[i - 1] 推导出来的,那么一定是从前到后遍历! 代码如下: -```C++ +```CPP for (int i = 2; i < nums.size(); i++) { dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]); } @@ -86,7 +86,7 @@ for (int i = 2; i < nums.size(); i++) { 以上分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int rob(vector& nums) { @@ -175,6 +175,22 @@ func max(a, b int) int { } ``` +JavaScript: + +```javascript +const rob = nums => { + // 数组长度 + const len = nums.length; + // dp数组初始化 + const dp = [nums[0], Math.max(nums[0], nums[1])]; + // 从下标2开始遍历 + for (let i = 2; i < len; i++) { + dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]); + } + return dp[len - 1]; +}; +``` + @@ -182,4 +198,4 @@ func max(a, b int) int { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0202.快乐数.md b/problems/0202.快乐数.md index d1bccd64..2e784e6a 100644 --- a/problems/0202.快乐数.md +++ b/problems/0202.快乐数.md @@ -46,7 +46,7 @@ https://leetcode-cn.com/problems/happy-number/ C++代码如下: -```C++ +```CPP class Solution { public: // 取数值各个位上的单数之和 @@ -111,25 +111,29 @@ Python: ```python class Solution: def isHappy(self, n: int) -> bool: - set_ = set() - while 1: - sum_ = self.getSum(n) - if sum_ == 1: + def calculate_happy(num): + sum_ = 0 + + # 从个位开始依次取,平方求和 + while num: + sum_ += (num % 10) ** 2 + num = num // 10 + return sum_ + + # 记录中间结果 + record = set() + + while True: + n = calculate_happy(n) + if n == 1: return True - #如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false - if sum_ in set_: + + # 如果中间结果重复出现,说明陷入死循环了,该数不是快乐数 + if n in record: return False else: - set_.add(sum_) - n = sum_ - - #取数值各个位上的单数之和 - def getSum(self, n): - sum_ = 0 - while n > 0: - sum_ += (n%10) * (n%10) - n //= 10 - return sum_ + record.add(n) + ``` Go: @@ -193,4 +197,4 @@ var isHappy = function(n) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0203.移除链表元素.md b/problems/0203.移除链表元素.md index cac9f233..9235d47e 100644 --- a/problems/0203.移除链表元素.md +++ b/problems/0203.移除链表元素.md @@ -89,7 +89,7 @@ https://leetcode-cn.com/problems/remove-linked-list-elements/ **直接使用原来的链表来进行移除节点操作:** -```C++ +```CPP class Solution { public: ListNode* removeElements(ListNode* head, int val) { @@ -118,7 +118,7 @@ public: **设置一个虚拟头结点在进行移除节点操作:** -```C++ +```CPP class Solution { public: ListNode* removeElements(ListNode* head, int val) { @@ -146,8 +146,39 @@ public: ## 其他语言版本 +C: +```c +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * struct ListNode *next; + * }; + */ +struct ListNode* removeElements(struct ListNode* head, int val){ + typedef struct ListNode ListNode; + ListNode *shead; + shead = (ListNode *)malloc(sizeof(ListNode)); + shead->next = head; + ListNode *cur = shead; + while(cur->next != NULL){ + if (cur->next->val == val){ + ListNode *tmp = cur->next; + cur->next = cur->next->next; + free(tmp); + } + else{ + cur = cur->next; + } + } + head = shead->next; + free(shead); + return head; +} +``` + Java: ```java /** @@ -273,6 +304,34 @@ var removeElements = function(head, val) { }; ``` +Swift: + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +func removeElements(_ head: ListNode?, _ val: Int) -> ListNode? { + let dummyNode = ListNode() + dummyNode.next = head + var currentNode = dummyNode + while let curNext = currentNode.next { + if curNext.val == val { + currentNode.next = curNext.next + } else { + currentNode = curNext + } + } + return dummyNode.next +} +``` + @@ -280,4 +339,4 @@ var removeElements = function(head, val) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0205.同构字符串.md b/problems/0205.同构字符串.md index 4e963ece..d5077e03 100644 --- a/problems/0205.同构字符串.md +++ b/problems/0205.同构字符串.md @@ -39,7 +39,7 @@ C++代码 如下: -```C++ +```CPP class Solution { public: bool isIsomorphic(string s, string t) { @@ -68,6 +68,25 @@ public: ## Java ```java +class Solution { + public boolean isIsomorphic(String s, String t) { + Map map1 = new HashMap<>(); + Map map2 = new HashMap<>(); + for (int i = 0, j = 0; i < s.length(); i++, j++) { + if (!map1.containsKey(s.charAt(i))) { + map1.put(s.charAt(i), t.charAt(j)); // map1保存 s[i] 到 t[j]的映射 + } + if (!map2.containsKey(t.charAt(j))) { + map2.put(t.charAt(j), s.charAt(i)); // map2保存 t[j] 到 s[i]的映射 + } + // 无法映射,返回 false + if (map1.get(s.charAt(i)) != t.charAt(j) || map2.get(t.charAt(j)) != s.charAt(i)) { + return false; + } + } + return true; + } +} ``` ## Python @@ -89,5 +108,5 @@ public: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0206.翻转链表.md b/problems/0206.翻转链表.md index 963d7916..8bb359bd 100644 --- a/problems/0206.翻转链表.md +++ b/problems/0206.翻转链表.md @@ -48,7 +48,7 @@ https://leetcode-cn.com/problems/reverse-linked-list/ # C++代码 ## 双指针法 -```C++ +```CPP class Solution { public: ListNode* reverseList(ListNode* head) { @@ -74,7 +74,7 @@ public: 关键是初始化的地方,可能有的同学会不理解, 可以看到双指针法中初始化 cur = head,pre = NULL,在递归法中可以从如下代码看出初始化的逻辑也是一样的,只不过写法变了。 具体可以看代码(已经详细注释),**双指针法写出来之后,理解如下递归写法就不难了,代码逻辑都是一样的。** -```C++ +```CPP class Solution { public: ListNode* reverse(ListNode* pre,ListNode* cur){ @@ -275,11 +275,67 @@ var reverseList = function(head) { }; ``` +Ruby: +```ruby +# 双指针 +# Definition for singly-linked list. +# class ListNode +# attr_accessor :val, :next +# def initialize(val = 0, _next = nil) +# @val = val +# @next = _next +# end +# end +def reverse_list(head) + # return nil if head.nil? # 循环判断条件亦能起到相同作用因此不必单独判断 + cur, per = head, nil + until cur.nil? + tem = cur.next + cur.next = per + per = cur + cur = tem + end + per +end +# 递归 +# Definition for singly-linked list. +# class ListNode +# attr_accessor :val, :next +# def initialize(val = 0, _next = nil) +# @val = val +# @next = _next +# end +# end +def reverse_list(head) + reverse(nil, head) +end + +def reverse(pre, cur) + return pre if cur.nil? + tem = cur.next + cur.next = pre + reverse(cur, tem) # 通过递归实现双指针法中的更新操作 +end +``` +Kotlin: +```Kotlin +fun reverseList(head: ListNode?): ListNode? { + var pre: ListNode? = null + var cur = head + while (cur != null) { + val temp = cur.next + cur.next = pre + pre = cur + cur = temp + } + return pre +} +``` ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0209.长度最小的子数组.md b/problems/0209.长度最小的子数组.md index 42687514..3219d16f 100644 --- a/problems/0209.长度最小的子数组.md +++ b/problems/0209.长度最小的子数组.md @@ -26,7 +26,7 @@ 代码如下: -```C++ +```CPP class Solution { public: int minSubArrayLen(int s, vector& nums) { @@ -86,7 +86,7 @@ public: C++代码如下: -```C++ +```CPP class Solution { public: int minSubArrayLen(int s, vector& nums) { @@ -216,8 +216,31 @@ var minSubArrayLen = function(target, nums) { }; ``` +Swift: + +```swift +func minSubArrayLen(_ target: Int, _ nums: [Int]) -> Int { + var result = Int.max + var sum = 0 + var starIndex = 0 + for endIndex in 0..= target { + result = min(result, endIndex - starIndex + 1) + sum -= nums[starIndex] + starIndex += 1 + } + } + + return result == Int.max ? 0 : result +} +``` + + + ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0213.打家劫舍II.md b/problems/0213.打家劫舍II.md index 12951117..e828d17a 100644 --- a/problems/0213.打家劫舍II.md +++ b/problems/0213.打家劫舍II.md @@ -59,7 +59,7 @@ 代码如下: -```C++ +```CPP // 注意注释中的情况二情况三,以及把198.打家劫舍的代码抽离出来了 class Solution { public: @@ -174,4 +174,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0216.组合总和III.md b/problems/0216.组合总和III.md index 15220464..c9cfa973 100644 --- a/problems/0216.组合总和III.md +++ b/problems/0216.组合总和III.md @@ -94,7 +94,7 @@ void backtracking(int targetSum, int k, int sum, int startIndex) 所以 终止代码如下: -```C++ +```CPP if (path.size() == k) { if (sum == targetSum) result.push_back(path); return; // 如果path.size() == k 但sum != targetSum 直接返回 @@ -112,7 +112,7 @@ if (path.size() == k) { 代码如下: -```C++ +```CPP for (int i = startIndex; i <= 9; i++) { sum += i; path.push_back(i); @@ -126,7 +126,7 @@ for (int i = startIndex; i <= 9; i++) { 参照[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)中的模板,不难写出如下C++代码: -```C++ +```CPP class Solution { private: vector> result; // 存放结果集 @@ -398,4 +398,4 @@ var combinationSum3 = function(k, n) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0222.完全二叉树的节点个数.md b/problems/0222.完全二叉树的节点个数.md index ec68b6c6..0d3a818e 100644 --- a/problems/0222.完全二叉树的节点个数.md +++ b/problems/0222.完全二叉树的节点个数.md @@ -7,24 +7,23 @@

欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

-## 222.完全二叉树的节点个数 +# 222.完全二叉树的节点个数 题目地址:https://leetcode-cn.com/problems/count-complete-tree-nodes/ 给出一个完全二叉树,求出该树的节点个数。 -示例: 示例 1: -输入:root = [1,2,3,4,5,6] -输出:6 +* 输入:root = [1,2,3,4,5,6] +* 输出:6 示例 2: -输入:root = [] -输出:0 +* 输入:root = [] +* 输出:0 示例 3: -输入:root = [1] -输出:1 +* 输入:root = [1] +* 输出:1 提示: @@ -33,21 +32,22 @@ * 题目数据保证输入的树是 完全二叉树 -## 思路 +# 思路 本篇给出按照普通二叉树的求法以及利用完全二叉树性质的求法。 + ## 普通二叉树 首先按照普通二叉树的逻辑来求。 -这道题目的递归法和求二叉树的深度写法类似, 而迭代法,[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog)遍历模板稍稍修改一下,记录遍历的节点数量就可以了。 +这道题目的递归法和求二叉树的深度写法类似, 而迭代法,[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/4-bDKi7SdwfBGRm9FYduiA)遍历模板稍稍修改一下,记录遍历的节点数量就可以了。 递归遍历的顺序依然是后序(左右中)。 ### 递归 -如果对求二叉树深度还不熟悉的话,看这篇:[二叉树:看看这些树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)。 +如果对求二叉树深度还不熟悉的话,看这篇:[二叉树:看看这些树的最大深度](https://mp.weixin.qq.com/s/jRaRcRerhEHepQbt-aKstw)。 1. 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回以该节点为根节点二叉树的节点数量,所以返回值为int类型。 @@ -77,7 +77,7 @@ return treeNum; 所以整体C++代码如下: -```C++ +```CPP // 版本一 class Solution { private: @@ -96,7 +96,7 @@ public: ``` 代码精简之后C++代码如下: -```C++ +```CPP // 版本二 class Solution { public: @@ -107,19 +107,19 @@ public: }; ``` -时间复杂度:O(n) -空间复杂度:O(logn),算上了递归系统栈占用的空间 +* 时间复杂度:O(n) +* 空间复杂度:O(logn),算上了递归系统栈占用的空间 **网上基本都是这个精简的代码版本,其实不建议大家照着这个来写,代码确实精简,但隐藏了一些内容,连遍历的顺序都看不出来,所以初学者建议学习版本一的代码,稳稳的打基础**。 ### 迭代法 -如果对求二叉树层序遍历还不熟悉的话,看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog)。 +如果对求二叉树层序遍历还不熟悉的话,看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/4-bDKi7SdwfBGRm9FYduiA)。 那么只要模板少做改动,加一个变量result,统计节点数量就可以了 -```C++ +```CPP class Solution { public: int countNodes(TreeNode* root) { @@ -140,12 +140,12 @@ public: } }; ``` -时间复杂度:O(n) -空间复杂度:O(n) +* 时间复杂度:O(n) +* 空间复杂度:O(n) ## 完全二叉树 -以上方法都是按照普通二叉树来做的,对于完全二叉树特性不了解的同学可以看这篇 [关于二叉树,你该了解这些!](https://mp.weixin.qq.com/s/_ymfWYvTNd2GvWvC5HOE4A),这篇详细介绍了各种二叉树的特性。 +以上方法都是按照普通二叉树来做的,对于完全二叉树特性不了解的同学可以看这篇 [关于二叉树,你该了解这些!](https://mp.weixin.qq.com/s/q_eKfL8vmSbSFcptZ3aeRA),这篇详细介绍了各种二叉树的特性。 完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。 @@ -163,7 +163,7 @@ public: C++代码如下: -```C++ +```CPP class Solution { public: int countNodes(TreeNode* root) { @@ -187,13 +187,12 @@ public: }; ``` -时间复杂度:O(logn * logn) -空间复杂度:O(logn) +* 时间复杂度:O(logn * logn) +* 空间复杂度:O(logn) -## 其他语言版本 +# 其他语言版本 - -Java: +## Java ```java class Solution { // 通用递归解法 @@ -238,9 +237,9 @@ class Solution { } ``` -Python: +## Python -> 递归法: +递归法: ```python class Solution: def countNodes(self, root: TreeNode) -> int: @@ -255,7 +254,7 @@ class Solution: return treeNum ``` -> 递归法:精简版 +递归法:精简版 ```python class Solution: def countNodes(self, root: TreeNode) -> int: @@ -264,7 +263,7 @@ class Solution: return 1 + self.countNodes(root.left) + self.countNodes(root.right) ``` -> 迭代法: +迭代法: ```python import collections class Solution: @@ -285,7 +284,7 @@ class Solution: return result ``` -> 完全二叉树 +完全二叉树 ```python class Solution: def countNodes(self, root: TreeNode) -> int: @@ -306,7 +305,7 @@ class Solution: return self.countNodes(root.left) + self.countNodes(root.right) + 1 ``` -Go: +## Go 递归版本 @@ -361,7 +360,7 @@ func countNodes(root *TreeNode) int { -JavaScript: +## JavaScript: 递归版本 ```javascript @@ -436,4 +435,4 @@ var countNodes = function(root) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0225.用队列实现栈.md b/problems/0225.用队列实现栈.md index b3e851a1..b327c17b 100644 --- a/problems/0225.用队列实现栈.md +++ b/problems/0225.用队列实现栈.md @@ -65,7 +65,7 @@ queue.empty(); 详细如代码注释所示: -```C++ +```CPP class MyStack { public: queue que1; @@ -118,7 +118,7 @@ public: C++优化代码 -```C++ +```CPP class MyStack { public: queue que; @@ -460,4 +460,4 @@ MyStack.prototype.empty = function() { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0226.翻转二叉树.md b/problems/0226.翻转二叉树.md index 44f0d3b4..6eb6f301 100644 --- a/problems/0226.翻转二叉树.md +++ b/problems/0226.翻转二叉树.md @@ -89,7 +89,7 @@ invertTree(root->right); 基于这递归三步法,代码基本写完,C++代码如下: -```C++ +```CPP class Solution { public: TreeNode* invertTree(TreeNode* root) { @@ -111,7 +111,7 @@ public: C++代码迭代法(前序遍历) -```C++ +```CPP class Solution { public: TreeNode* invertTree(TreeNode* root) { @@ -136,7 +136,7 @@ public: C++代码如下迭代法(前序遍历) -```C++ +```CPP class Solution { public: TreeNode* invertTree(TreeNode* root) { @@ -168,7 +168,7 @@ public: 也就是层序遍历,层数遍历也是可以翻转这棵树的,因为层序遍历也可以把每个节点的左右孩子都翻转一遍,代码如下: -```C++ +```CPP class Solution { public: TreeNode* invertTree(TreeNode* root) { @@ -196,7 +196,7 @@ public: 如果非要使用递归中序的方式写,也可以,如下代码就可以避免节点左右孩子翻转两次的情况: -```C++ +```CPP class Solution { public: TreeNode* invertTree(TreeNode* root) { @@ -215,7 +215,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: TreeNode* invertTree(TreeNode* root) { @@ -478,4 +478,4 @@ var invertTree = function(root) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0232.用栈实现队列.md b/problems/0232.用栈实现队列.md index 6890fc2b..0df82d35 100644 --- a/problems/0232.用栈实现队列.md +++ b/problems/0232.用栈实现队列.md @@ -67,7 +67,7 @@ queue.empty(); C++代码如下: -```C++ +```CPP class MyQueue { public: stack stIn; @@ -384,7 +384,7 @@ func (this *MyQueue) Peek() int { func (this *MyQueue) Empty() bool { return len(this.stack) == 0 && len(this.back) == 0 } - +``` javaScript: @@ -442,10 +442,8 @@ MyQueue.prototype.empty = function() { ``` - - ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0234.回文链表.md b/problems/0234.回文链表.md index 945d2ef4..b3ad899c 100644 --- a/problems/0234.回文链表.md +++ b/problems/0234.回文链表.md @@ -30,7 +30,7 @@ 代码也比较简单。如下: -```C++ +```CPP class Solution { public: bool isPalindrome(ListNode* head) { @@ -51,7 +51,7 @@ public: 上面代码可以在优化,就是先求出链表长度,然后给定vector的初始长度,这样避免vector每次添加节点重新开辟空间 -```C++ +```CPP class Solution { public: bool isPalindrome(ListNode* head) { @@ -95,7 +95,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: bool isPalindrome(ListNode* head) { @@ -148,7 +148,62 @@ public: ## Python -```python +```python3 +#数组模拟 +class Solution: + def isPalindrome(self, head: ListNode) -> bool: + length = 0 + tmp = head + while tmp: #求链表长度 + length += 1 + tmp = tmp.next + + result = [0] * length + tmp = head + index = 0 + while tmp: #链表元素加入数组 + result[index] = tmp.val + index += 1 + tmp = tmp.next + + i, j = 0, length - 1 + while i < j: # 判断回文 + if result[i] != result[j]: + return False + i += 1 + j -= 1 + return True + +#反转后半部分链表 +class Solution: + def isPalindrome(self, head: ListNode) -> bool: + if head == None or head.next == None: + return True + slow, fast = head, head + while fast and fast.next: + pre = slow + slow = slow.next + fast = fast.next.next + + pre.next = None # 分割链表 + cur1 = head # 前半部分 + cur2 = self.reverseList(slow) # 反转后半部分,总链表长度如果是奇数,cur2比cur1多一个节点 + while cur1: + if cur1.val != cur2.val: + return False + cur1 = cur1.next + cur2 = cur2.next + return True + + def reverseList(self, head: ListNode) -> ListNode: + cur = head + pre = None + while(cur!=None): + temp = cur.next # 保存一下cur的下一个节点 + cur.next = pre # 反转 + pre = cur + cur = temp + return pre ``` ## Go @@ -166,5 +221,5 @@ public: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0235.二叉搜索树的最近公共祖先.md b/problems/0235.二叉搜索树的最近公共祖先.md index dffc89e6..fe875067 100644 --- a/problems/0235.二叉搜索树的最近公共祖先.md +++ b/problems/0235.二叉搜索树的最近公共祖先.md @@ -93,7 +93,7 @@ if (cur == NULL) return cur; 代码如下: -```C++ +```CPP if (cur->val > p->val && cur->val > q->val) { TreeNode* left = traversal(cur->left, p, q); if (left != NULL) { @@ -147,7 +147,7 @@ return cur; 那么整体递归代码如下: -```C++ +```CPP class Solution { private: TreeNode* traversal(TreeNode* cur, TreeNode* p, TreeNode* q) { @@ -177,7 +177,7 @@ public: 精简后代码如下: -```C++ +```CPP class Solution { public: TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { @@ -198,7 +198,7 @@ public: 迭代代码如下: -```C++ +```CPP class Solution { public: TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { @@ -360,4 +360,4 @@ var lowestCommonAncestor = function(root, p, q) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0236.二叉树的最近公共祖先.md b/problems/0236.二叉树的最近公共祖先.md index 7b5deb56..0885e20f 100644 --- a/problems/0236.二叉树的最近公共祖先.md +++ b/problems/0236.二叉树的最近公共祖先.md @@ -150,7 +150,7 @@ TreeNode* right = lowestCommonAncestor(root->right, p, q); 代码如下: -```C++ +```CPP if (left == NULL && right != NULL) return right; else if (left != NULL && right == NULL) return left; else { // (left == NULL && right == NULL) @@ -167,7 +167,7 @@ else { // (left == NULL && right == NULL) 整体代码如下: -```C++ +```CPP class Solution { public: TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { @@ -188,7 +188,7 @@ public: 稍加精简,代码如下: -```C++ +```CPP class Solution { public: TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { @@ -249,7 +249,6 @@ class Solution { ```java // 代码精简版 class Solution { - TreeNode pre; public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { if (root == null || root.val == p.val ||root.val == q.val) return root; TreeNode left = lowestCommonAncestor(root.left,p,q); @@ -343,4 +342,4 @@ var lowestCommonAncestor = function(root, p, q) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0239.滑动窗口最大值.md b/problems/0239.滑动窗口最大值.md index dedc3247..3c12a985 100644 --- a/problems/0239.滑动窗口最大值.md +++ b/problems/0239.滑动窗口最大值.md @@ -108,7 +108,7 @@ public: 基于刚刚说过的单调队列pop和push的规则,代码不难实现,如下: -```C++ +```CPP class MyQueue { //单调队列(从大到小) public: deque que; // 使用deque来实现单调队列 @@ -140,7 +140,7 @@ public: C++代码如下: -```C++ +```CPP class Solution { private: class MyQueue { //单调队列(从大到小) @@ -425,4 +425,4 @@ var maxSlidingWindow = function (nums, k) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0242.有效的字母异位词.md b/problems/0242.有效的字母异位词.md index 93bba44c..b215a88a 100644 --- a/problems/0242.有效的字母异位词.md +++ b/problems/0242.有效的字母异位词.md @@ -61,7 +61,7 @@ https://leetcode-cn.com/problems/valid-anagram/ C++ 代码如下: -```C++ +```CPP class Solution { public: bool isAnagram(string s, string t) { @@ -209,4 +209,4 @@ var isAnagram = function(s, t) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0257.二叉树的所有路径.md b/problems/0257.二叉树的所有路径.md index ce596396..84315507 100644 --- a/problems/0257.二叉树的所有路径.md +++ b/problems/0257.二叉树的所有路径.md @@ -9,7 +9,7 @@ > 以为只用了递归,其实还用了回溯 -## 257. 二叉树的所有路径 +# 257. 二叉树的所有路径 题目地址:https://leetcode-cn.com/problems/binary-tree-paths/ @@ -20,7 +20,7 @@ 示例: ![257.二叉树的所有路径1](https://img-blog.csdnimg.cn/2021020415161576.png) -## 思路 +# 思路 这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。 @@ -77,7 +77,7 @@ if (cur->left == NULL && cur->right == NULL) { 这里我们先使用vector结构的path容器来记录路径,那么终止处理逻辑如下: -```C++ +```CPP if (cur->left == NULL && cur->right == NULL) { // 遇到叶子节点 string sPath; for (int i = 0; i < path.size() - 1; i++) { // 将path里记录的路径转为string格式 @@ -113,7 +113,7 @@ if (cur->right) { 那么回溯要怎么回溯呢,一些同学会这么写,如下: -```C++ +```CPP if (cur->left) { traversal(cur->left, path, result); } @@ -129,7 +129,7 @@ path.pop_back(); 那么代码应该这么写: -```C++ +```CPP if (cur->left) { traversal(cur->left, path, result); path.pop_back(); // 回溯 @@ -142,7 +142,7 @@ if (cur->right) { 那么本题整体代码如下: -```C++ +```CPP class Solution { private: @@ -183,7 +183,7 @@ public: 那么如上代码可以精简成如下代码: -```C++ +```CPP class Solution { private: @@ -215,8 +215,52 @@ public: 那么在如上代码中,**貌似没有看到回溯的逻辑,其实不然,回溯就隐藏在`traversal(cur->left, path + "->", result);`中的 `path + "->"`。** 每次函数调用完,path依然是没有加上"->" 的,这就是回溯了。 -**如果这里还不理解的话,可以看这篇[二叉树:以为使用了递归,其实还隐藏着回溯](https://mp.weixin.qq.com/s/ivLkHzWdhjQQD1rQWe6zWA),我这这篇中详细的解释了递归中如何隐藏着回溯。 ** +为了把这份精简代码的回溯过程展现出来,大家可以试一试把: +```CPP +if (cur->left) traversal(cur->left, path + "->", result); // 左 回溯就隐藏在这里 +``` + +改成如下代码: + +```CPP +path += "->"; +traversal(cur->left, path, result); // 左 +``` + +即: + +```CPP +if (cur->left) { + path += "->"; + traversal(cur->left, path, result); // 左 +} +if (cur->right) { + path += "->"; + traversal(cur->right, path, result); // 右 +} +``` + +此时就没有回溯了,这个代码就是通过不了的了。 + +如果想把回溯加上,就要 在上面代码的基础上,加上回溯,就可以AC了。 + +```CPP +if (cur->left) { + path += "->"; + traversal(cur->left, path, result); // 左 + path.pop_back(); // 回溯 + path.pop_back(); +} +if (cur->right) { + path += "->"; + traversal(cur->right, path, result); // 右 + path.pop_back(); // 回溯 + path.pop_back(); +} +``` + +**大家应该可以感受出来,如果把 `path + "->"`作为函数参数就是可以的,因为并有没有改变path的数值,执行完递归函数之后,path依然是之前的数值(相当于回溯了)** **综合以上,第二种递归的代码虽然精简但把很多重要的点隐藏在了代码细节里,第一种递归写法虽然代码多一些,但是把每一个逻辑处理都完整的展现了出来了。** @@ -225,13 +269,14 @@ public: ## 迭代法 -至于非递归的方式,我们可以依然可以使用前序遍历的迭代方式来模拟遍历路径的过程,对该迭代方式不了解的同学,可以看文章[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg)和[二叉树:前中后序迭代方式的写法就不能统一一下么?](https://mp.weixin.qq.com/s/WKg0Ty1_3SZkztpHubZPRg)。 + +至于非递归的方式,我们可以依然可以使用前序遍历的迭代方式来模拟遍历路径的过程,对该迭代方式不了解的同学,可以看文章[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/OH7aCVJ5-Gi32PkNCoZk4A)和[二叉树:前中后序迭代方式统一写法](https://mp.weixin.qq.com/s/ATQMPCpBlaAgrqdLDMVPZA)。 这里除了模拟递归需要一个栈,同时还需要一个栈来存放对应的遍历路径。 C++代码如下: -```C++ +```CPP class Solution { public: vector binaryTreePaths(TreeNode* root) { @@ -262,7 +307,7 @@ public: ``` 当然,使用java的同学,可以直接定义一个成员变量为object的栈`Stack stack = new Stack<>();`,这样就不用定义两个栈了,都放到一个栈里就可以了。 -## 总结 +# 总结 **本文我们开始初步涉及到了回溯,很多同学过了这道题目,可能都不知道自己其实使用了回溯,回溯和递归都是相伴相生的。** @@ -278,7 +323,7 @@ public: -## 其他语言版本 +# 其他语言版本 Java: @@ -321,62 +366,10 @@ class Solution { } } } - -//解法二(常规前序遍历,不用回溯),更容易理解 -class Solution { - public List binaryTreePaths(TreeNode root) { - List res = new ArrayList<>(); - helper(root, new StringBuilder(), res); - return res; - } - - public void helper(TreeNode root, StringBuilder sb, List res) { - if (root == null) {return;} - // 遇到叶子结点就放入当前路径到res集合中 - if (root.left == null && root.right ==null) { - sb.append(root.val); - res.add(sb.toString()); - // 记得结束当前方法 - return; - } - helper(root.left,new StringBuilder(sb).append(root.val + "->"),res); - helper(root.right,new StringBuilder(sb).append(root.val + "->"),res); - } -} - -//针对解法二优化,思路本质是一样的 -class Solution { - public List binaryTreePaths(TreeNode root) { - List res = new ArrayList<>(); - helper(root, "", res); - return res; - } - - public void helper(TreeNode root, String path, List res) { - if (root == null) {return;} - // 由原始解法二可以知道,root的值肯定会下面某一个条件加入到path中,那么干脆直接在这一步加入即可 - StringBuilder sb = new StringBuilder(path); - sb.append(root.val); - if (root.left == null && root.right ==null) { - res.add(sb.toString()); - }else{ - // 如果是非叶子结点则还需要跟上一个 “->” - sb.append("->"); - helper(root.left,sb.toString(),res); - helper(root.right,sb.toString(),res); - } - } -} - ``` Python: ```Python -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right class Solution: def binaryTreePaths(self, root: TreeNode) -> List[str]: path=[] @@ -396,8 +389,8 @@ class Solution: return ["->".join(list(map(str,i))) for i in res] ``` -Go: +Go: ```go func binaryTreePaths(root *TreeNode) []string { res := make([]string, 0) @@ -422,7 +415,9 @@ func binaryTreePaths(root *TreeNode) []string { ``` JavaScript: + 1.递归版本 + ```javascript var binaryTreePaths = function(root) { //递归遍历+递归三部曲 @@ -452,4 +447,4 @@ var binaryTreePaths = function(root) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0279.完全平方数.md b/problems/0279.完全平方数.md index d0922de1..e51f0a99 100644 --- a/problems/0279.完全平方数.md +++ b/problems/0279.完全平方数.md @@ -76,7 +76,7 @@ dp[0]表示 和为0的完全平方数的最小数量,那么dp[0]一定是0。 我这里先给出外层遍历背包,里层遍历物品的代码: -```C++ +```CPP vector dp(n + 1, INT_MAX); dp[0] = 0; for (int i = 0; i <= n; i++) { // 遍历背包 @@ -106,7 +106,7 @@ dp[5] = min(dp[4] + 1, dp[1] + 1) = 2 以上动规五部曲分析完毕C++代码如下: -```C++ +```CPP // 版本一 class Solution { public: @@ -125,7 +125,7 @@ public: 同样我在给出先遍历物品,在遍历背包的代码,一样的可以AC的。 -```C++ +```CPP // 版本二 class Solution { public: @@ -286,10 +286,38 @@ func min(a, b int) int { } ``` +Javascript: +```Javascript +// 先遍历物品,再遍历背包 +var numSquares1 = function(n) { + let dp = new Array(n + 1).fill(Infinity) + dp[0] = 0 + for(let i = 0; i <= n; i++) { + let val = i * i + for(let j = val; j <= n; j++) { + dp[j] = Math.min(dp[j], dp[j - val] + 1) + } + } + return dp[n] +}; +// 先遍历背包,再遍历物品 +var numSquares2 = function(n) { + let dp = new Array(n + 1).fill(Infinity) + dp[0] = 0 + + for(let i = 1; i <= n; i++) { + for(let j = 1; j * j <= i; j++) { + dp[i] = Math.min(dp[i - j * j] + 1, dp[i]) + } + } + + return dp[n] +}; +``` ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0283.移动零.md b/problems/0283.移动零.md index 56f96b2f..af7142fa 100644 --- a/problems/0283.移动零.md +++ b/problems/0283.移动零.md @@ -42,7 +42,7 @@ C++代码如下: -```C++ +```CPP class Solution { public: void moveZeroes(vector& nums) { @@ -64,6 +64,21 @@ public: Java: +```java +public void moveZeroes(int[] nums) { + int slow = 0; + for (int fast = 0; fast < nums.length; fast++) { + if (nums[fast] != 0) { + nums[slow++] = nums[fast]; + } + } + // 后面的元素全变成 0 + for (int j = slow; j < nums.length; j++) { + nums[j] = 0; + } + } +``` + Python: ```python @@ -85,5 +100,5 @@ JavaScript: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0300.最长上升子序列.md b/problems/0300.最长上升子序列.md index 9a7c543b..57edd13e 100644 --- a/problems/0300.最长上升子序列.md +++ b/problems/0300.最长上升子序列.md @@ -60,7 +60,7 @@ dp[i] 是有0到i-1各个位置的最长升序子序列 推导而来,那么遍 j其实就是0到i-1,遍历i的循环里外层,遍历j则在内层,代码如下: -```C++ +```CPP for (int i = 1; i < nums.size(); i++) { for (int j = 0; j < i; j++) { if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1); @@ -80,7 +80,7 @@ for (int i = 1; i < nums.size(); i++) { 以上五部分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int lengthOfLIS(vector& nums) { @@ -198,4 +198,4 @@ const lengthOfLIS = (nums) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0309.最佳买卖股票时机含冷冻期.md b/problems/0309.最佳买卖股票时机含冷冻期.md index e28e8369..3b1b6500 100644 --- a/problems/0309.最佳买卖股票时机含冷冻期.md +++ b/problems/0309.最佳买卖股票时机含冷冻期.md @@ -95,7 +95,7 @@ p[i][3] = dp[i - 1][2]; 综上分析,递推代码如下: -```C++ +```CPP dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3], dp[i - 1][1]) - prices[i]; dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]); dp[i][2] = dp[i - 1][0] + prices[i]; @@ -129,7 +129,7 @@ dp[i][3] = dp[i - 1][2]; 代码如下: -```C++ +```CPP class Solution { public: int maxProfit(vector& prices) { @@ -236,4 +236,4 @@ const maxProfit = (prices) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0322.零钱兑换.md b/problems/0322.零钱兑换.md index 302ae789..758bd5b8 100644 --- a/problems/0322.零钱兑换.md +++ b/problems/0322.零钱兑换.md @@ -112,7 +112,7 @@ dp[amount]为最终结果。 ## C++代码 以上分析完毕,C++ 代码如下: -```C++ +```CPP // 版本一 class Solution { public: @@ -134,7 +134,7 @@ public: 对于遍历方式遍历背包放在外循环,遍历物品放在内循环也是可以的,我就直接给出代码了 -```C++ +```CPP // 版本二 class Solution { public: @@ -330,4 +330,4 @@ const coinChange = (coins, amount) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0332.重新安排行程.md b/problems/0332.重新安排行程.md index 3c7e34d8..8a28899b 100644 --- a/problems/0332.重新安排行程.md +++ b/problems/0332.重新安排行程.md @@ -178,7 +178,7 @@ if (result.size() == ticketNum + 1) { 遍历过程如下: -```C++ +```CPP for (pair& target : targets[result[result.size() - 1]]) { if (target.second > 0 ) { // 记录到达机场是否飞过了 result.push_back(target.first); @@ -194,7 +194,7 @@ for (pair& target : targets[result[result.size() - 1]]) { 分析完毕,此时完整C++代码如下: -```C++ +```CPP class Solution { private: // unordered_map<出发机场, map<到达机场, 航班次数>> targets @@ -450,4 +450,4 @@ var findItinerary = function(tickets) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0337.打家劫舍III.md b/problems/0337.打家劫舍III.md index 197841e9..44e60fb3 100644 --- a/problems/0337.打家劫舍III.md +++ b/problems/0337.打家劫舍III.md @@ -35,7 +35,7 @@ 代码如下: -```C++ +```CPP class Solution { public: int rob(TreeNode* root) { @@ -65,7 +65,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: unordered_map umap; // 记录计算过的结果 @@ -103,7 +103,7 @@ public: 参数为当前节点,代码如下: -```C++ +```CPP vector robTree(TreeNode* cur) { ``` @@ -138,7 +138,7 @@ if (cur == NULL) return vector{0, 0}; 代码如下: -```C++ +```CPP // 下标0:不偷,下标1:偷 vector left = robTree(cur->left); // 左 vector right = robTree(cur->right); // 右 @@ -156,7 +156,7 @@ vector right = robTree(cur->right); // 右 代码如下: -```C++ +```CPP vector left = robTree(cur->left); // 左 vector right = robTree(cur->right); // 右 @@ -179,7 +179,7 @@ return {val2, val1}; 递归三部曲与动规五部曲分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int rob(TreeNode* root) { @@ -368,7 +368,32 @@ class Solution: return (val1, val2) ``` -Go: +JavaScript: + +> 动态规划 + +```javascript +const rob = root => { + // 后序遍历函数 + const postOrder = node => { + // 递归出口 + if (!node) return [0, 0]; + // 遍历左子树 + const left = postOrder(node.left); + // 遍历右子树 + const right = postOrder(node.right); + // 不偷当前节点,左右子节点都可以偷或不偷,取最大值 + const DoNot = Math.max(left[0], left[1]) + Math.max(right[0], right[1]); + // 偷当前节点,左右子节点只能不偷 + const Do = node.val + left[0] + right[0]; + // [不偷,偷] + return [DoNot, Do]; + }; + const res = postOrder(root); + // 返回最大值 + return Math.max(...res); +}; +``` @@ -377,4 +402,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0343.整数拆分.md b/problems/0343.整数拆分.md index 7b0dbd0f..a9c55a85 100644 --- a/problems/0343.整数拆分.md +++ b/problems/0343.整数拆分.md @@ -59,6 +59,10 @@ j是从1开始遍历,拆分j的情况,在遍历j的过程中其实都计算 所以递推公式:dp[i] = max({dp[i], (i - j) * j, dp[i - j] * j}); +那么在取最大值的时候,为什么还要比较dp[i]呢? + +因为在递推公式推导的过程中,每次计算dp[i],取最大的而已。 + 3. dp的初始化 @@ -101,7 +105,7 @@ for (int i = 3; i <= n ; i++) { 以上动规五部曲分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int integerBreak(int n) { @@ -128,7 +132,7 @@ public: 给出我的C++代码如下: -```C++ +```CPP class Solution { public: int integerBreak(int n) { @@ -154,7 +158,7 @@ public: 其实这道题目的递推公式并不好想,而且初始化的地方也很有讲究,我在写本题的时候一开始写的代码是这样的: -```C++ +```CPP class Solution { public: int integerBreak(int n) { @@ -244,4 +248,4 @@ var integerBreak = function(n) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0344.反转字符串.md b/problems/0344.反转字符串.md index aeb13a30..4f96f839 100644 --- a/problems/0344.反转字符串.md +++ b/problems/0344.反转字符串.md @@ -74,7 +74,7 @@ https://leetcode-cn.com/problems/reverse-string/ 不难写出如下C++代码: -```C++ +```CPP void reverseString(vector& s) { for (int i = 0, j = s.size() - 1; i < s.size()/2; i++, j--) { swap(s[i],s[j]); @@ -90,7 +90,7 @@ swap可以有两种实现。 一种就是常见的交换数值: -```C++ +```CPP int tmp = s[i]; s[i] = s[j]; s[j] = tmp; @@ -99,7 +99,7 @@ s[j] = tmp; 一种就是通过位运算: -```C++ +```CPP s[i] ^= s[j]; s[j] ^= s[i]; s[i] ^= s[j]; @@ -120,7 +120,7 @@ s[i] ^= s[j]; C++代码如下: -```C++ +```CPP class Solution { public: void reverseString(vector& s) { @@ -219,4 +219,4 @@ var reverseString = function(s) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0347.前K个高频元素.md b/problems/0347.前K个高频元素.md index 6b07c258..680de3bd 100644 --- a/problems/0347.前K个高频元素.md +++ b/problems/0347.前K个高频元素.md @@ -76,7 +76,7 @@ https://leetcode-cn.com/problems/top-k-frequent-elements/ 我们来看一下C++代码: -```C++ +```CPP // 时间复杂度:O(nlogk) // 空间复杂度:O(n) class Solution { @@ -190,10 +190,105 @@ class Solution: Go: +javaScript: +```js +/** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ +var topKFrequent = function(nums, k) { + const map = new Map(); + + for(const num of nums) { + map.set(num, (map.get(num) || 0) + 1); + } + + // 创建小顶堆 + const priorityQueue = new PriorityQueue((a, b) => a[1] - b[1]); + + // entry 是一个长度为2的数组,0位置存储key,1位置存储value + for (const entry of map.entries()) { + priorityQueue.push(entry); + if (priorityQueue.size() > k) { + priorityQueue.pop(); + } + } + + const ret = []; + + for(let i = priorityQueue.size() - 1; i >= 0; i--) { + ret[i] = priorityQueue.pop()[0]; + } + + return ret; +}; + + +function PriorityQueue(compareFn) { + this.compareFn = compareFn; + this.queue = []; +} + +// 添加 +PriorityQueue.prototype.push = function(item) { + this.queue.push(item); + let index = this.queue.length - 1; + let parent = Math.floor((index - 1) / 2); + // 上浮 + while(parent >= 0 && this.compare(parent, index) > 0) { + // 交换 + [this.queue[index], this.queue[parent]] = [this.queue[parent], this.queue[index]]; + index = parent; + parent = Math.floor((index - 1) / 2); + } +} + +// 获取堆顶元素并移除 +PriorityQueue.prototype.pop = function() { + const ret = this.queue[0]; + + // 把最后一个节点移到堆顶 + this.queue[0] = this.queue.pop(); + + let index = 0; + // 左子节点下标,left + 1 就是右子节点下标 + let left = 1; + let selectedChild = this.compare(left, left + 1) > 0 ? left + 1 : left; + + // 下沉 + while(selectedChild !== undefined && this.compare(index, selectedChild) > 0) { + // 交换 + [this.queue[index], this.queue[selectedChild]] = [this.queue[selectedChild], this.queue[index]]; + index = selectedChild; + left = 2 * index + 1; + selectedChild = this.compare(left, left + 1) > 0 ? left + 1 : left; + } + + return ret; +} + +PriorityQueue.prototype.size = function() { + return this.queue.length; +} + +// 使用传入的 compareFn 比较两个位置的元素 +PriorityQueue.prototype.compare = function(index1, index2) { + if (this.queue[index1] === undefined) { + return 1; + } + if (this.queue[index2] === undefined) { + return -1; + } + + return this.compareFn(this.queue[index1], this.queue[index2]); +} +``` + ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0349.两个数组的交集.md b/problems/0349.两个数组的交集.md index 5c635d39..7489352d 100644 --- a/problems/0349.两个数组的交集.md +++ b/problems/0349.两个数组的交集.md @@ -54,7 +54,7 @@ std::set和std::multiset底层实现都是红黑树,std::unordered_set的底 C++代码如下: -```C++ +```CPP class Solution { public: vector intersection(vector& nums1, vector& nums2) { @@ -121,13 +121,7 @@ Python: ```python class Solution: def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: - result_set = set() - - set1 = set(nums1) - for num in nums2: - if num in set1: - result_set.add(num) # set1里出现的nums2元素 存放到结果 - return result_set + return list(set(nums1) & set(nums2)) # 两个数组先变成集合,求交集后还原为数组 ``` @@ -188,4 +182,4 @@ var intersection = function(nums1, nums2) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0376.摆动序列.md b/problems/0376.摆动序列.md index f64e0043..0ccc405a 100644 --- a/problems/0376.摆动序列.md +++ b/problems/0376.摆动序列.md @@ -70,7 +70,7 @@ C++代码如下(和上图是对应的逻辑): -```C++ +```CPP class Solution { public: int wiggleMaxLength(vector& nums) { @@ -192,4 +192,4 @@ var wiggleMaxLength = function(nums) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0377.组合总和Ⅳ.md b/problems/0377.组合总和Ⅳ.md index 2ee009b3..3f735727 100644 --- a/problems/0377.组合总和Ⅳ.md +++ b/problems/0377.组合总和Ⅳ.md @@ -107,7 +107,7 @@ dp[i](考虑nums[j])可以由 dp[i - nums[j]](不考虑nums[j]) 推导 以上分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int combinationSum4(vector& nums, int target) { @@ -226,4 +226,4 @@ const combinationSum4 = (nums, target) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0383.赎金信.md b/problems/0383.赎金信.md index a5315b0e..d4b73848 100644 --- a/problems/0383.赎金信.md +++ b/problems/0383.赎金信.md @@ -40,7 +40,7 @@ canConstruct("aa", "aab") -> true 那么第一个思路其实就是暴力枚举了,两层for循环,不断去寻找,代码如下: -```C++ +```CPP // 时间复杂度: O(n^2) // 空间复杂度:O(1) class Solution { @@ -79,7 +79,7 @@ public: 代码如下: -```C++ +```CPP // 时间复杂度: O(n) // 空间复杂度:O(1) class Solution { @@ -209,6 +209,22 @@ class Solution(object): return True ``` +Python写法四: + +```python3 +class Solution: + def canConstruct(self, ransomNote: str, magazine: str) -> bool: + c1 = collections.Counter(ransomNote) + c2 = collections.Counter(magazine) + x = c1 - c2 + #x只保留值大于0的符号,当c1里面的符号个数小于c2时,不会被保留 + #所以x只保留下了,magazine不能表达的 + if(len(x)==0): + return True + else: + return False +``` + Go: ```go @@ -256,4 +272,4 @@ var canConstruct = function(ransomNote, magazine) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0392.判断子序列.md b/problems/0392.判断子序列.md index d97d2684..bcf3462b 100644 --- a/problems/0392.判断子序列.md +++ b/problems/0392.判断子序列.md @@ -107,7 +107,7 @@ dp[i][j]表示以下标i-1为结尾的字符串s和以下标j-1为结尾的字 动规五部曲分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: bool isSubsequence(string s, string t) { @@ -180,7 +180,30 @@ class Solution: return False ``` -Go: +JavaScript: + +```javascript +const isSubsequence = (s, t) => { + // s、t的长度 + const [m, n] = [s.length, t.length]; + // dp全初始化为0 + const dp = new Array(m + 1).fill(0).map(x => new Array(n + 1).fill(0)); + for (let i = 1; i <= m; i++) { + for (let j = 1; j <= n; j++) { + // 更新dp[i][j],两种情况 + if (s[i - 1] === t[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = dp[i][j - 1]; + } + } + } + // 遍历结束,判断dp右下角的数是否等于s的长度 + return dp[m][n] === m ? true : false; +}; +``` + + @@ -189,4 +212,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0404.左叶子之和.md b/problems/0404.左叶子之和.md index aa758367..84ec2310 100644 --- a/problems/0404.左叶子之和.md +++ b/problems/0404.左叶子之和.md @@ -7,7 +7,7 @@

欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

-## 404.左叶子之和 +# 404.左叶子之和 题目地址:https://leetcode-cn.com/problems/sum-of-left-leaves/ @@ -17,7 +17,7 @@ ![404.左叶子之和1](https://img-blog.csdnimg.cn/20210204151927654.png) -## 思路 +# 思路 **首先要注意是判断左叶子,不是二叉树左侧节点,所以不要上来想着层序遍历。** @@ -65,7 +65,7 @@ if (root == NULL) return 0; 代码如下: -```C++ +```CPP int leftValue = sumOfLeftLeaves(root->left); // 左 int rightValue = sumOfLeftLeaves(root->right); // 右 // 中 @@ -81,7 +81,7 @@ return sum; 整体递归代码如下: -```C++ +```CPP class Solution { public: int sumOfLeftLeaves(TreeNode* root) { @@ -102,7 +102,7 @@ public: 以上代码精简之后如下: -```C++ +```CPP class Solution { public: int sumOfLeftLeaves(TreeNode* root) { @@ -119,11 +119,11 @@ public: ## 迭代法 -本题迭代法使用前中后序都是可以的,只要把左叶子节点统计出来,就可以了,那么参考文章 [二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg)和[二叉树:前中后序迭代方式的写法就不能统一一下么?](https://mp.weixin.qq.com/s/WKg0Ty1_3SZkztpHubZPRg)中的写法,可以写出一个前序遍历的迭代法。 +本题迭代法使用前中后序都是可以的,只要把左叶子节点统计出来,就可以了,那么参考文章 [二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/OH7aCVJ5-Gi32PkNCoZk4A)和[二叉树:迭代法统一写法](https://mp.weixin.qq.com/s/ATQMPCpBlaAgrqdLDMVPZA)中的写法,可以写出一个前序遍历的迭代法。 判断条件都是一样的,代码如下: -```C++ +```CPP class Solution { public: @@ -146,7 +146,7 @@ public: }; ``` -## 总结 +# 总结 这道题目要求左叶子之和,其实是比较绕的,因为不能判断本节点是不是左叶子节点。 @@ -157,9 +157,9 @@ public: 希望通过这道题目,可以扩展大家对二叉树的解题思路。 -## 其他语言版本 +# 其他语言版本 -Java: +## Java **递归** @@ -204,39 +204,57 @@ class Solution { -Python: -```Python +## Python + **递归** -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right +```python class Solution: def sumOfLeftLeaves(self, root: TreeNode) -> int: - self.res=0 - def areleftleaves(root): - if not root:return - if root.left and (not root.left.left) and (not root.left.right):self.res+=root.left.val - areleftleaves(root.left) - areleftleaves(root.right) - areleftleaves(root) - return self.res + if not root: + return 0 + + left_left_leaves_sum = self.sumOfLeftLeaves(root.left) # 左 + right_left_leaves_sum = self.sumOfLeftLeaves(root.right) # 右 + + cur_left_leaf_val = 0 + if root.left and not root.left.left and not root.left.right: + cur_left_leaf_val = root.left.val # 中 + + return cur_left_leaf_val + left_left_leaves_sum + right_left_leaves_sum ``` -Go: -> 递归法 +**迭代** +```python +class Solution: + def sumOfLeftLeaves(self, root: TreeNode) -> int: + """ + Idea: Each time check current node's left node. + If current node don't have one, skip it. + """ + stack = [] + if root: + stack.append(root) + res = 0 + + while stack: + # 每次都把当前节点的左节点加进去. + cur_node = stack.pop() + if cur_node.left and not cur_node.left.left and not cur_node.left.right: + res += cur_node.left.val + + if cur_node.left: + stack.append(cur_node.left) + if cur_node.right: + stack.append(cur_node.right) + + return res +``` + +## Go + +**递归法** ```go -/** - * Definition for a binary tree node. - * type TreeNode struct { - * Val int - * Left *TreeNode - * Right *TreeNode - * } - */ func sumOfLeftLeaves(root *TreeNode) int { var res int findLeft(root,&res) @@ -256,17 +274,9 @@ func findLeft(root *TreeNode,res *int){ } ``` -> 迭代法 +**迭代法** ```go -/** - * Definition for a binary tree node. - * type TreeNode struct { - * Val int - * Left *TreeNode - * Right *TreeNode - * } - */ func sumOfLeftLeaves(root *TreeNode) int { var res int queue:=list.New() @@ -291,8 +301,10 @@ func sumOfLeftLeaves(root *TreeNode) int { ``` -JavaScript: -递归版本 +## JavaScript + +**递归法** + ```javascript var sumOfLeftLeaves = function(root) { //采用后序遍历 递归遍历 @@ -314,8 +326,9 @@ var sumOfLeftLeaves = function(root) { } return nodesSum(root); }; -``` -迭代版本 +``` + +**迭代法** ```javascript var sumOfLeftLeaves = function(root) { //采用层序遍历 @@ -345,4 +358,4 @@ var sumOfLeftLeaves = function(root) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0406.根据身高重建队列.md b/problems/0406.根据身高重建队列.md index a5f66a5d..e9b9a738 100644 --- a/problems/0406.根据身高重建队列.md +++ b/problems/0406.根据身高重建队列.md @@ -99,7 +99,7 @@ C++代码如下: -```C++ +```CPP // 版本一 class Solution { public: @@ -127,7 +127,7 @@ public: 改成链表之后,C++代码如下: -```C++ +```CPP // 版本二 class Solution { public: @@ -217,7 +217,25 @@ class Solution: ``` Go: - +```golang +func reconstructQueue(people [][]int) [][]int { + //先将身高从大到小排序,确定最大个子的相对位置 + sort.Slice(people,func(i,j int)bool{ + if people[i][0]==people[j][0]{ + return people[i][1]people[j][0]//这个只是确保身高按照由大到小的顺序来排,并不确定K是按照从小到大排序的 + }) + //再按照K进行插入排序,优先插入K小的 + result := make([][]int, 0) + for _, info := range people { + result = append(result, info) + copy(result[info[1] +1:], result[info[1]:])//将插入位置之后的元素后移动一位(意思是腾出空间) + result[info[1]] = info//将插入元素位置插入元素 + } + return result +} +``` Javascript: ```Javascript var reconstructQueue = function(people) { @@ -243,4 +261,4 @@ var reconstructQueue = function(people) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0416.分割等和子集.md b/problems/0416.分割等和子集.md index 75c665cd..0f1f094a 100644 --- a/problems/0416.分割等和子集.md +++ b/problems/0416.分割等和子集.md @@ -106,7 +106,7 @@ 代码如下: -```C++ +```CPP // 题目中说:每个数组中的元素不会超过 100,数组的大小不会超过 200 // 总和不会大于20000,背包最大只需要其中一半,所以10001大小就可以了 vector dp(10001, 0); @@ -118,7 +118,7 @@ vector dp(10001, 0); 代码如下: -```C++ +```CPP // 开始 01背包 for(int i = 0; i < nums.size(); i++) { for(int j = target; j >= nums[i]; j--) { // 每一个元素一定是不可重复放入,所以从大到小遍历 @@ -141,7 +141,7 @@ dp[i]的数值一定是小于等于i的。 综上分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: bool canPartition(vector& nums) { @@ -266,4 +266,4 @@ var canPartition = function(nums) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0435.无重叠区间.md b/problems/0435.无重叠区间.md index 37bb0b5d..70d0ff3a 100644 --- a/problems/0435.无重叠区间.md +++ b/problems/0435.无重叠区间.md @@ -126,7 +126,7 @@ public: 把[452.用最少数量的箭引爆气球](https://mp.weixin.qq.com/s/HxVAJ6INMfNKiGwI88-RFw)代码稍做修改,就可以AC本题。 -```C++ +```CPP class Solution { public: // 按照区间右边界排序 @@ -152,7 +152,7 @@ public: ``` 这里按照 左区间遍历,或者按照右边界遍历,都可以AC,具体原因我还没有仔细看,后面有空再补充。 -```C++ +```CPP class Solution { public: // 按照区间左边界排序 @@ -211,6 +211,29 @@ class Solution { } ``` +Java: +按左边排序,不管右边顺序。相交的时候取最小的右边。 +```java +class Solution { + public int eraseOverlapIntervals(int[][] intervals) { + + Arrays.sort(intervals,(a,b)->{ + return Integer.compare(a[0],b[0]); + }); + int remove = 0; + int pre = intervals[0][1]; + for(int i=1;iintervals[i][0]) { + remove++; + pre = Math.min(pre,intervals[i][1]); + } + else pre = intervals[i][1]; + } + return remove; + } +} +``` + Python: ```python class Solution: @@ -227,7 +250,29 @@ class Solution: ``` Go: - +```golang +func eraseOverlapIntervals(intervals [][]int) int { + var flag int + //先排序 + sort.Slice(intervals,func(i,j int)bool{ + return intervals[i][0]intervals[i][0]{ + flag++ + intervals[i][1]=min(intervals[i-1][1],intervals[i][1])//由于是先排序的,所以,第一位是递增顺序,故只需要将临近两个元素的第二个值最小值更新到该元素的第二个值即可作之后的判断 + } + } + return flag +} +func min(a,b int)int{ + if a>b{ + return b + } + return a +} +``` Javascript: ```Javascript var eraseOverlapIntervals = function(intervals) { @@ -255,4 +300,4 @@ var eraseOverlapIntervals = function(intervals) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0450.删除二叉搜索树中的节点.md b/problems/0450.删除二叉搜索树中的节点.md index 4695ed50..96c49468 100644 --- a/problems/0450.删除二叉搜索树中的节点.md +++ b/problems/0450.删除二叉搜索树中的节点.md @@ -78,7 +78,7 @@ if (root == nullptr) return root; 代码如下: -```C++ +```CPP if (root->val == key) { // 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点 // 第三种情况:其左孩子为空,右孩子不为空,删除节点,右孩子补位 ,返回右孩子为根节点 @@ -111,7 +111,7 @@ return root; **整体代码如下:(注释中:情况1,2,3,4,5和上面分析严格对应)** -```C++ +```CPP class Solution { public: TreeNode* deleteNode(TreeNode* root, int key) { @@ -156,7 +156,7 @@ public: 代码如下:(关键部分已经注释) -```C++ +```CPP class Solution { public: TreeNode* deleteNode(TreeNode* root, int key) { @@ -186,7 +186,7 @@ public: 代码如下: -```C++ +```CPP class Solution { private: // 将目标节点(删除节点)的左子树放到 目标节点的右子树的最左面节点的左孩子位置上 @@ -320,6 +320,7 @@ class Solution: Go: ```Go +// 递归版本 func deleteNode(root *TreeNode, key int) *TreeNode { if root==nil{ return nil @@ -356,6 +357,51 @@ func deleteNode1(root *TreeNode)*TreeNode{ root.Left=deleteNode1(root.Left) return root } +// 迭代版本 +func deleteOneNode(target *TreeNode) *TreeNode { + if target == nil { + return target + } + if target.Right == nil { + return target.Left + } + cur := target.Right + for cur.Left != nil { + cur = cur.Left + } + cur.Left = target.Left + return target.Right +} +func deleteNode(root *TreeNode, key int) *TreeNode { + // 特殊情况处理 + if root == nil { + return root + } + cur := root + var pre *TreeNode + for cur != nil { + if cur.Val == key { + break + } + pre = cur + if cur.Val > key { + cur = cur.Left + } else { + cur = cur.Right + } + } + if pre == nil { + return deleteOneNode(cur) + } + // pre 要知道是删除左孩子还有右孩子 + if pre.Left != nil && pre.Left.Val == key { + pre.Left = deleteOneNode(cur) + } + if pre.Right != nil && pre.Right.Val == key { + pre.Right = deleteOneNode(cur) + } + return root +} ``` JavaScript版本 @@ -410,4 +456,4 @@ var deleteNode = function (root, key) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0452.用最少数量的箭引爆气球.md b/problems/0452.用最少数量的箭引爆气球.md index bb3ebbdc..589105b2 100644 --- a/problems/0452.用最少数量的箭引爆气球.md +++ b/problems/0452.用最少数量的箭引爆气球.md @@ -84,7 +84,7 @@ C++代码如下: -```C++ +```CPP class Solution { private: static bool cmp(const vector& a, const vector& b) { @@ -175,6 +175,30 @@ class Solution: Go: +```golang +func findMinArrowShots(points [][]int) int { + var res int =1//弓箭数 + //先按照第一位排序 + sort.Slice(points,func (i,j int) bool{ + return points[i][0]b{ + return b + } + return a +} +``` Javascript: ```Javascript var findMinArrowShots = function(points) { @@ -199,4 +223,4 @@ var findMinArrowShots = function(points) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0454.四数相加II.md b/problems/0454.四数相加II.md index 0621ab5b..eafee43e 100644 --- a/problems/0454.四数相加II.md +++ b/problems/0454.四数相加II.md @@ -53,7 +53,7 @@ D = [ 0, 2] C++代码: -```C++ +```CPP class Solution { public: int fourSumCount(vector& A, vector& B, vector& C, vector& D) { @@ -234,4 +234,4 @@ var fourSumCount = function(nums1, nums2, nums3, nums4) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0455.分发饼干.md b/problems/0455.分发饼干.md index 6b121e36..19268942 100644 --- a/problems/0455.分发饼干.md +++ b/problems/0455.分发饼干.md @@ -59,7 +59,7 @@ C++代码整体如下: -```C++ +```CPP // 时间复杂度:O(nlogn) // 空间复杂度:O(1) class Solution { @@ -88,7 +88,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: int findContentChildren(vector& g, vector& s) { @@ -188,4 +188,4 @@ var findContentChildren = function(g, s) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0459.重复的子字符串.md b/problems/0459.重复的子字符串.md index f012811d..3b735942 100644 --- a/problems/0459.重复的子字符串.md +++ b/problems/0459.重复的子字符串.md @@ -70,7 +70,7 @@ next[len - 1] = 7,next[len - 1] + 1 = 8,8就是此时字符串asdfasdfasdf C++代码如下:(这里使用了前缀表统一减一的实现方式) -```C++ +```CPP class Solution { public: void getNext (int* next, const string& s){ @@ -104,7 +104,7 @@ public: 前缀表(不减一)的C++代码实现 -```C++ +```CPP class Solution { public: void getNext (int* next, const string& s){ @@ -289,8 +289,79 @@ func repeatedSubstringPattern(s string) bool { } ``` +JavaScript版本 +> 前缀表统一减一 +```javascript +/** + * @param {string} s + * @return {boolean} + */ +var repeatedSubstringPattern = function (s) { + if (s.length === 0) + return false; + + const getNext = (s) => { + let next = []; + let j = -1; + + next.push(j); + + for (let i = 1; i < s.length; ++i) { + while (j >= 0 && s[i] !== s[j + 1]) + j = next[j]; + if (s[i] === s[j + 1]) + j++; + next.push(j); + } + + return next; + } + + let next = getNext(s); + + if (next[next.length - 1] !== -1 && s.length % (s.length - (next[next.length - 1] + 1)) === 0) + return true; + return false; +}; +``` + +> 前缀表统一不减一 + +```javascript +/** + * @param {string} s + * @return {boolean} + */ +var repeatedSubstringPattern = function (s) { + if (s.length === 0) + return false; + + const getNext = (s) => { + let next = []; + let j = 0; + + next.push(j); + + for (let i = 1; i < s.length; ++i) { + while (j > 0 && s[i] !== s[j]) + j = next[j - 1]; + if (s[i] === s[j]) + j++; + next.push(j); + } + + return next; + } + + let next = getNext(s); + + if (next[next.length - 1] !== 0 && s.length % (s.length - next[next.length - 1]) === 0) + return true; + return false; +}; +``` @@ -298,4 +369,4 @@ func repeatedSubstringPattern(s string) bool { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0463.岛屿的周长.md b/problems/0463.岛屿的周长.md index 40378854..15255d3e 100644 --- a/problems/0463.岛屿的周长.md +++ b/problems/0463.岛屿的周长.md @@ -16,7 +16,7 @@ https://leetcode-cn.com/problems/island-perimeter/ C++代码如下:(详细注释) -```C++ +```CPP class Solution { public: int direction[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; @@ -56,7 +56,7 @@ result = 岛屿数量 * 4 - cover * 2; C++代码如下:(详细注释) -```C++ +```CPP class Solution { public: int islandPerimeter(vector>& grid) { @@ -95,4 +95,4 @@ JavaScript: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0474.一和零.md b/problems/0474.一和零.md index 6b73c080..90d1227a 100644 --- a/problems/0474.一和零.md +++ b/problems/0474.一和零.md @@ -95,7 +95,7 @@ dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。 那么本题也是,物品就是strs里的字符串,背包容量就是题目描述中的m和n。 代码如下: -```C++ +```CPP for (string str : strs) { // 遍历物品 int oneNum = 0, zeroNum = 0; for (char c : str) { @@ -126,7 +126,7 @@ for (string str : strs) { // 遍历物品 以上动规五部曲分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int findMaxForm(vector& strs, int m, int n) { @@ -279,4 +279,4 @@ const findMaxForm = (strs, m, n) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0491.递增子序列.md b/problems/0491.递增子序列.md index 103b8b10..ed712f34 100644 --- a/problems/0491.递增子序列.md +++ b/problems/0491.递增子序列.md @@ -105,7 +105,7 @@ for (int i = startIndex; i < nums.size(); i++) { 最后整体C++代码如下: -```C++ +```CPP // 版本一 class Solution { private: @@ -150,7 +150,7 @@ public: 那么优化后的代码如下: -```C++ +```CPP // 版本二 class Solution { private: @@ -320,4 +320,4 @@ var findSubsequences = function(nums) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0494.目标和.md b/problems/0494.目标和.md index b7da0252..77b3c76d 100644 --- a/problems/0494.目标和.md +++ b/problems/0494.目标和.md @@ -67,7 +67,7 @@ target是固定的,sum是固定的,left就可以求出来。 我也把代码给出来吧,大家可以了解一下,回溯的解法,以下是本题转变为组合总和问题的回溯法代码: -```C++ +```CPP class Solution { private: vector> result; @@ -124,7 +124,7 @@ x = (S + sum) / 2 这么担心就对了,例如sum 是5,S是2的话其实就是无解的,所以: -```C++ +```CPP if ((S + sum) % 2 == 1) return 0; // 此时没有方案 ``` @@ -194,7 +194,7 @@ dp数组状态变化如下: C++代码如下: -```C++ +```CPP class Solution { public: int findTargetSumWays(vector& nums, int S) { @@ -342,4 +342,4 @@ const findTargetSumWays = (nums, target) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0496.下一个更大元素I.md b/problems/0496.下一个更大元素I.md index 0404e434..589ee8ad 100644 --- a/problems/0496.下一个更大元素I.md +++ b/problems/0496.下一个更大元素I.md @@ -64,7 +64,7 @@ C++中,当我们要使用集合来解决哈希问题的时候,优先使用un 那么预处理代码如下: -```C++ +```CPP unordered_map umap; // key:下表元素,value:下表 for (int i = 0; i < nums1.size(); i++) { umap[nums1[i]] = i; @@ -100,7 +100,7 @@ for (int i = 0; i < nums1.size(); i++) { 代码如下: -```C++ +```CPP while (!st.empty() && nums2[i] > nums2[st.top()]) { if (umap.count(nums2[st.top()]) > 0) { // 看map里是否存在这个元素 int index = umap[nums2[st.top()]]; // 根据map找到nums2[st.top()] 在 nums1中的下表 @@ -114,7 +114,7 @@ st.push(i); 以上分析完毕,C++代码如下: -```C++ +```CPP // 版本一 class Solution { public: @@ -152,7 +152,7 @@ public: 针对版本一,进行代码精简后,代码如下: -```C++ +```CPP // 版本二 class Solution { public: @@ -186,7 +186,37 @@ public: 建议大家把情况一二三想清楚了,先写出版本一的代码,然后在其基础上在做精简! ## 其他语言版本 +Java +```java +class Solution { + public int[] nextGreaterElement(int[] nums1, int[] nums2) { + Stack temp = new Stack<>(); + int[] res = new int[nums1.length]; + Arrays.fill(res,-1); + HashMap hashMap = new HashMap<>(); + for (int i = 0 ; i< nums1.length ; i++){ + hashMap.put(nums1[i],i); + } + temp.add(0); + for (int i = 1; i < nums2.length; i++) { + if (nums2[i] <= nums2[temp.peek()]) { + temp.add(i); + } else { + while (!temp.isEmpty() && nums2[temp.peek()] < nums2[i]) { + if (hashMap.containsKey(nums2[temp.peek()])){ + Integer index = hashMap.get(nums2[temp.peek()]); + res[index] = nums2[i]; + } + temp.pop(); + } + temp.add(i); + } + } + return res; + } +} +``` Python: ```python3 class Solution: @@ -208,3 +238,4 @@ class Solution: return result ``` +
diff --git a/problems/0501.二叉搜索树中的众数.md b/problems/0501.二叉搜索树中的众数.md index ac7ff603..f29fd08a 100644 --- a/problems/0501.二叉搜索树中的众数.md +++ b/problems/0501.二叉搜索树中的众数.md @@ -53,7 +53,7 @@ 这里采用前序遍历,代码如下: -```C++ +```CPP // map key:元素,value:出现频率 void searchBST(TreeNode* cur, unordered_map& map) { // 前序遍历 if (cur == NULL) return ; @@ -87,7 +87,7 @@ sort(vec.begin(), vec.end(), cmp); // 给频率排个序 代码如下: -```C++ +```CPP result.push_back(vec[0].first); for (int i = 1; i < vec.size(); i++) { // 取最高的放到result数组中 @@ -100,7 +100,7 @@ return result; 整体C++代码如下: -```C++ +```CPP class Solution { private: @@ -145,7 +145,7 @@ public: 中序遍历代码如下: -```C++ +```CPP void searchBST(TreeNode* cur) { if (cur == NULL) return ; searchBST(cur->left); // 左 @@ -217,7 +217,7 @@ if (count > maxCount) { // 如果计数大于最大值 关键代码都讲完了,完整代码如下:(**只需要遍历一遍二叉搜索树,就求出了众数的集合**) -```C++ +```CPP class Solution { private: int maxCount; // 最大频率 @@ -279,7 +279,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: vector findMode(TreeNode* root) { @@ -690,4 +690,4 @@ var findMode = function(root) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0503.下一个更大元素II.md b/problems/0503.下一个更大元素II.md index 34ade48e..9f38d80d 100644 --- a/problems/0503.下一个更大元素II.md +++ b/problems/0503.下一个更大元素II.md @@ -32,7 +32,7 @@ 代码如下: -```C++ +```CPP // 版本一 class Solution { public: @@ -69,7 +69,7 @@ resize倒是不费时间,是O(1)的操作,但扩充nums数组相当于多了 代码如下: -```C++ +```CPP // 版本二 class Solution { public: @@ -95,6 +95,28 @@ public: ## 其他语言版本 Java: +```Java +class Solution { + public int[] nextGreaterElements(int[] nums) { + //边界判断 + if(nums == null || nums.length <= 1) { + return new int[]{-1}; + } + int size = nums.length; + int[] result = new int[size];//存放结果 + Arrays.fill(result,-1);//默认全部初始化为-1 + Stack st= new Stack<>();//栈中存放的是nums中的元素下标 + for(int i = 0; i < 2*size; i++) { + while(!st.empty() && nums[i % size] > nums[st.peek()]) { + result[st.peek()] = nums[i % size];//更新result + st.pop();//弹出栈顶 + } + st.push(i % size); + } + return result; + } +} +``` Python: ```python3 @@ -112,3 +134,28 @@ class Solution: Go: JavaScript: + +```JS +/** + * @param {number[]} nums + * @return {number[]} + */ +var nextGreaterElements = function (nums) { + // let map = new Map(); + let stack = []; + let res = new Array(nums.length).fill(-1); + for (let i = 0; i < nums.length * 2; i++) { + while ( + stack.length && + nums[i % nums.length] > nums[stack[stack.length - 1]] + ) { + let index = stack.pop(); + res[index] = nums[i % nums.length]; + } + stack.push(i % nums.length); + } + + return res; +}; +``` +
diff --git a/problems/0509.斐波那契数.md b/problems/0509.斐波那契数.md index dddac899..3b3d5056 100644 --- a/problems/0509.斐波那契数.md +++ b/problems/0509.斐波那契数.md @@ -88,7 +88,7 @@ dp[1] = 1; 以上我们用动规的方法分析完了,C++代码如下: -```C++ +```CPP class Solution { public: int fib(int N) { @@ -110,7 +110,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: int fib(int N) { @@ -137,7 +137,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: int fib(int N) { @@ -238,4 +238,4 @@ var fib = function(n) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0513.找树左下角的值.md b/problems/0513.找树左下角的值.md index 17d15fde..bca7e074 100644 --- a/problems/0513.找树左下角的值.md +++ b/problems/0513.找树左下角的值.md @@ -7,7 +7,9 @@

欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

-## 513.找树左下角的值 +# 513.找树左下角的值 + +题目地址:[https://leetcode-cn.com/problems/find-bottom-left-tree-value/](https://leetcode-cn.com/problems/find-bottom-left-tree-value/v) 给定一个二叉树,在树的最后一行找到最左边的值。 @@ -19,7 +21,7 @@ ![513.找树左下角的值1](https://img-blog.csdnimg.cn/20210204153017586.png) -## 思路 +# 思路 本地要找出树的最后一行找到最左边的值。此时大家应该想起用层序遍历是非常简单的了,反而用递归的话会比较难一点。 @@ -37,7 +39,7 @@ 如果使用递归法,如何判断是最后一行呢,其实就是深度最大的叶子节点一定是最后一行。 -如果对二叉树深度和高度还有点疑惑的话,请看:[110.平衡二叉树](https://mp.weixin.qq.com/s/isUS-0HDYknmC0Rr4R8mww)。 +如果对二叉树深度和高度还有点疑惑的话,请看:[110.平衡二叉树](https://mp.weixin.qq.com/s/7QeWnxaAB66LjFJOs40XKg)。 所以要找深度最大的叶子节点。 @@ -53,7 +55,7 @@ 代码如下: -``` +```CPP int maxLen = INT_MIN; // 全局变量 记录最大深度 int maxleftValue; // 全局变量 最大深度最左节点的数值 void traversal(TreeNode* root, int leftLen) @@ -75,7 +77,7 @@ void traversal(TreeNode* root, int leftLen) 代码如下: -``` +```CPP if (root->left == NULL && root->right == NULL) { if (leftLen > maxLen) { maxLen = leftLen; // 更新最大深度 @@ -89,7 +91,7 @@ if (root->left == NULL && root->right == NULL) { 在找最大深度的时候,递归的过程中依然要使用回溯,代码如下: -```C++ +```CPP // 中 if (root->left) { // 左 leftLen++; // 深度加一 @@ -106,7 +108,7 @@ return; 完整代码如下: -```C++ +```CPP class Solution { public: int maxLen = INT_MIN; @@ -140,7 +142,7 @@ public: 当然回溯的地方可以精简,精简代码如下: -```C++ +```CPP class Solution { public: int maxLen = INT_MIN; @@ -168,7 +170,7 @@ public: }; ``` -如果对回溯部分精简的代码 不理解的话,可以看这篇[二叉树:找我的所有路径?](https://mp.weixin.qq.com/s/Osw4LQD2xVUnCJ-9jrYxJA)和[二叉树:以为使用了递归,其实还隐藏着回溯](https://mp.weixin.qq.com/s/ivLkHzWdhjQQD1rQWe6zWA) 。这两篇文章详细分析了回溯隐藏在了哪里。 +如果对回溯部分精简的代码 不理解的话,可以看这篇[257. 二叉树的所有路径](https://mp.weixin.qq.com/s/-x0IL-5eb9W0kZC1-TM0Lw) ## 迭代法 @@ -177,11 +179,11 @@ public: 只需要记录最后一行第一个节点的数值就可以了。 -如果对层序遍历不了解,看这篇[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog),这篇里也给出了层序遍历的模板,稍作修改就一过刷了这道题了。 +如果对层序遍历不了解,看这篇[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/4-bDKi7SdwfBGRm9FYduiA),这篇里也给出了层序遍历的模板,稍作修改就一过刷了这道题了。 代码如下: -```C++ +```CPP class Solution { public: int findBottomLeftValue(TreeNode* root) { @@ -203,20 +205,20 @@ public: }; ``` -## 总结 +# 总结 本题涉及如下几点: -* 递归求深度的写法,我们在[110.平衡二叉树](https://mp.weixin.qq.com/s/isUS-0HDYknmC0Rr4R8mww)中详细的分析了深度应该怎么求,高度应该怎么求。 -* 递归中其实隐藏了回溯,在[二叉树:以为使用了递归,其实还隐藏着回溯](https://mp.weixin.qq.com/s/ivLkHzWdhjQQD1rQWe6zWA)中讲解了究竟哪里使用了回溯,哪里隐藏了回溯。 -* 层次遍历,在[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog)深度讲解了二叉树层次遍历。 +* 递归求深度的写法,我们在[110.平衡二叉树](https://mp.weixin.qq.com/s/7QeWnxaAB66LjFJOs40XKg)中详细的分析了深度应该怎么求,高度应该怎么求。 +* 递归中其实隐藏了回溯,在[257. 二叉树的所有路径](https://mp.weixin.qq.com/s/-x0IL-5eb9W0kZC1-TM0Lw)中讲解了究竟哪里使用了回溯,哪里隐藏了回溯。 +* 层次遍历,在[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/4-bDKi7SdwfBGRm9FYduiA)深度讲解了二叉树层次遍历。 所以本题涉及到的点,我们之前都讲解过,这些知识点需要同学们灵活运用,这样就举一反三了。 -## 其他语言版本 +# 其他语言版本 -Java: +## Java ```java // 递归法 @@ -273,42 +275,60 @@ class Solution { -Python: +## Python + +递归: ```python -//递归法 -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right class Solution: def findBottomLeftValue(self, root: TreeNode) -> int: - depth=0 - self.res=[] - def level(root,depth): - if not root:return - if depth==len(self.res): - self.res.append([]) - self.res[depth].append(root.val) - level(root.left,depth+1) - level(root.right,depth+1) - level(root,depth) - return self.res[-1][0] -``` -Go: + max_depth = -float("INF") + leftmost_val = 0 -> 递归法 + def __traverse(root, cur_depth): + nonlocal max_depth, leftmost_val + if not root.left and not root.right: + if cur_depth > max_depth: + max_depth = cur_depth + leftmost_val = root.val + if root.left: + cur_depth += 1 + __traverse(root.left, cur_depth) + cur_depth -= 1 + if root.right: + cur_depth += 1 + __traverse(root.right, cur_depth) + cur_depth -= 1 + + __traverse(root, 0) + return leftmost_val +``` + +迭代 - 层序遍历: +```python +class Solution: + def findBottomLeftValue(self, root: TreeNode) -> int: + queue = deque() + if root: + queue.append(root) + result = 0 + while queue: + q_len = len(queue) + for i in range(q_len): + if i == 0: + result = queue[i].val + cur = queue.popleft() + if cur.left: + queue.append(cur.left) + if cur.right: + queue.append(cur.right) + return result +``` + +## Go + +递归法: ```go -/** - * Definition for a binary tree node. - * type TreeNode struct { - * Val int - * Left *TreeNode - * Right *TreeNode - * } - */ var maxDeep int // 全局变量 深度 var value int //全局变量 最终值 func findBottomLeftValue(root *TreeNode) int { @@ -340,17 +360,9 @@ func findLeftValue (root *TreeNode,deep int){ } ``` -> 迭代法 +迭代法: ```go -/** - * Definition for a binary tree node. - * type TreeNode struct { - * Val int - * Left *TreeNode - * Right *TreeNode - * } - */ func findBottomLeftValue(root *TreeNode) int { queue:=list.New() var gradation int @@ -372,8 +384,10 @@ func findBottomLeftValue(root *TreeNode) int { } ``` -JavaScript: -1. 递归版本 +## JavaScript + +递归版本: + ```javascript var findBottomLeftValue = function(root) { //首先考虑递归遍历 前序遍历 找到最大深度的叶子节点即可 @@ -395,7 +409,8 @@ var findBottomLeftValue = function(root) { return resNode; }; ``` -2. 层序遍历 +层序遍历: + ```javascript var findBottomLeftValue = function(root) { //考虑层序遍历 记录最后一行的第一个节点 @@ -426,4 +441,4 @@ var findBottomLeftValue = function(root) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0516.最长回文子序列.md b/problems/0516.最长回文子序列.md index 388d8d6a..54cea10e 100644 --- a/problems/0516.最长回文子序列.md +++ b/problems/0516.最长回文子序列.md @@ -69,7 +69,7 @@ 代码如下: -```C++ +```CPP if (s[i] == s[j]) { dp[i][j] = dp[i + 1][j - 1] + 2; } else { @@ -85,7 +85,7 @@ if (s[i] == s[j]) { 其他情况dp[i][j]初始为0就行,这样递推公式:dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]); 中dp[i][j]才不会被初始值覆盖。 -```C++ +```CPP vector> dp(s.size(), vector(s.size(), 0)); for (int i = 0; i < s.size(); i++) dp[i][i] = 1; ``` @@ -102,7 +102,7 @@ for (int i = 0; i < s.size(); i++) dp[i][i] = 1; 代码如下: -```C++ +```CPP for (int i = s.size() - 1; i >= 0; i--) { for (int j = i + 1; j < s.size(); j++) { if (s[i] == s[j]) { @@ -124,7 +124,7 @@ for (int i = s.size() - 1; i >= 0; i--) { 以上分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int longestPalindromeSubseq(string s) { @@ -243,4 +243,4 @@ const longestPalindromeSubseq = (s) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0518.零钱兑换II.md b/problems/0518.零钱兑换II.md index 3f907da9..ea2b1777 100644 --- a/problems/0518.零钱兑换II.md +++ b/problems/0518.零钱兑换II.md @@ -111,7 +111,7 @@ dp[j] (考虑coins[i]的组合总和) 就是所有的dp[j - coins[i]](不 代码如下: -```C++ +```CPP for (int i = 0; i < coins.size(); i++) { // 遍历物品 for (int j = coins[i]; j <= amount; j++) { // 遍历背包容量 dp[j] += dp[j - coins[i]]; @@ -151,7 +151,7 @@ for (int j = 0; j <= amount; j++) { // 遍历背包容量 以上分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int change(int amount, vector& coins) { @@ -265,4 +265,4 @@ const change = (amount, coins) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0530.二叉搜索树的最小绝对差.md b/problems/0530.二叉搜索树的最小绝对差.md index bf646443..04c03b3f 100644 --- a/problems/0530.二叉搜索树的最小绝对差.md +++ b/problems/0530.二叉搜索树的最小绝对差.md @@ -39,7 +39,7 @@ 代码如下: -```C++ +```CPP class Solution { private: vector vec; @@ -75,7 +75,7 @@ public: 代码如下: -```C++ +```CPP class Solution { private: int result = INT_MAX; @@ -105,7 +105,7 @@ public: 下面我给出其中的一种中序遍历的迭代法,代码如下: -```C++ +```CPP class Solution { public: int getMinimumDifference(TreeNode* root) { @@ -337,4 +337,4 @@ var getMinimumDifference = function (root) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0538.把二叉搜索树转换为累加树.md b/problems/0538.把二叉搜索树转换为累加树.md index 3ecb8195..d22cb4bc 100644 --- a/problems/0538.把二叉搜索树转换为累加树.md +++ b/problems/0538.把二叉搜索树转换为累加树.md @@ -107,7 +107,7 @@ traversal(cur->left); // 左 递归法整体代码如下: -```C++ +```CPP class Solution { private: int pre; // 记录前一个节点的数值 @@ -133,7 +133,7 @@ public: 这里我给出其中的一种,代码如下: -```C++ +```CPP class Solution { private: int pre; // 记录前一个节点的数值 @@ -308,4 +308,4 @@ var convertBST = function (root) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0541.反转字符串II.md b/problems/0541.反转字符串II.md index 47e85161..ab1ef16a 100644 --- a/problems/0541.反转字符串II.md +++ b/problems/0541.反转字符串II.md @@ -46,7 +46,7 @@ https://leetcode-cn.com/problems/reverse-string-ii/ 使用C++库函数reverse的版本如下: -```C++ +```CPP class Solution { public: string reverseStr(string s, int k) { @@ -69,7 +69,7 @@ public: 下面我实现的reverse函数区间是左闭右闭区间,代码如下: -```C++ +```CPP class Solution { public: void reverse(string& s, int start, int end) { @@ -239,4 +239,4 @@ var reverseStr = function(s, k) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0583.两个字符串的删除操作.md b/problems/0583.两个字符串的删除操作.md index 9aef2310..f6c13039 100644 --- a/problems/0583.两个字符串的删除操作.md +++ b/problems/0583.两个字符串的删除操作.md @@ -56,7 +56,7 @@ dp[i][0]:word2为空字符串,以i-1为结尾的字符串word2要删除多 dp[0][j]的话同理,所以代码如下: -```C++ +```CPP vector> dp(word1.size() + 1, vector(word2.size() + 1)); for (int i = 0; i <= word1.size(); i++) dp[i][0] = i; for (int j = 0; j <= word2.size(); j++) dp[0][j] = j; @@ -78,7 +78,7 @@ for (int j = 0; j <= word2.size(); j++) dp[0][j] = j; 以上分析完毕,代码如下: -```C++ +```CPP class Solution { public: int minDistance(string word1, string word2) { @@ -181,4 +181,4 @@ const minDistance = (word1, word2) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0617.合并二叉树.md b/problems/0617.合并二叉树.md index 19b58bd3..b24a934b 100644 --- a/problems/0617.合并二叉树.md +++ b/problems/0617.合并二叉树.md @@ -90,7 +90,7 @@ return t1; 此时前序遍历,完整代码就写出来了,如下: -```C++ +```CPP class Solution { public: TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) { @@ -107,7 +107,7 @@ public: 那么中序遍历也是可以的,代码如下: -```C++ +```CPP class Solution { public: TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) { @@ -124,7 +124,7 @@ public: 后序遍历依然可以,代码如下: -```C++ +```CPP class Solution { public: TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) { @@ -145,7 +145,7 @@ public: 不修改输入树的结构,前序遍历,代码如下: -```C++ +```CPP class Solution { public: TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) { @@ -169,7 +169,7 @@ public: 本题我们也使用队列,模拟的层序遍历,代码如下: -```C++ +```CPP class Solution { public: TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) { @@ -216,7 +216,7 @@ public: 如下代码中,想要更改二叉树的值,应该传入指向指针的指针。 代码如下:(前序遍历) -```C++ +```CPP class Solution { public: void process(TreeNode** t1, TreeNode** t2) { @@ -312,6 +312,8 @@ class Solution { ``` Python: + +**递归法 - 前序遍历** ```python # Definition for a binary tree node. # class TreeNode: @@ -319,41 +321,57 @@ Python: # self.val = val # self.left = left # self.right = right -# 递归法*前序遍历 class Solution: def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode: - if not root1: return root2 // 如果t1为空,合并之后就应该是t2 - if not root2: return root1 // 如果t2为空,合并之后就应该是t1 - root1.val = root1.val + root2.val //中 - root1.left = self.mergeTrees(root1.left , root2.left) //左 - root1.right = self.mergeTrees(root1.right , root2.right) //右 - return root1 //root1修改了结构和数值 + # 递归终止条件: + # 但凡有一个节点为空, 就立刻返回另外一个. 如果另外一个也为None就直接返回None. + if not root1: + return root2 + if not root2: + return root1 + # 上面的递归终止条件保证了代码执行到这里root1, root2都非空. + root1.val += root2.val # 中 + root1.left = self.mergeTrees(root1.left, root2.left) #左 + root1.right = self.mergeTrees(root1.right, root2.right) # 右 + + return root1 # ⚠️ 注意: 本题我们重复使用了题目给出的节点而不是创建新节点. 节省时间, 空间. -# 迭代法-覆盖原来的树 +``` + +**迭代法** +```python class Solution: def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode: - if not root1: return root2 - if not root2: return root1 - # 迭代,将树2覆盖到树1 - queue1 = [root1] - queue2 = [root2] - root = root1 - while queue1 and queue2: - root1 = queue1.pop(0) - root2 = queue2.pop(0) - root1.val += root2.val - if not root1.left: # 如果树1左儿子不存在,则覆盖后树1的左儿子为树2的左儿子 - root1.left = root2.left - elif root1.left and root2.left: - queue1.append(root1.left) - queue2.append(root2.left) + if not root1: + return root2 + if not root2: + return root1 - if not root1.right: # 同理,处理右儿子 - root1.right = root2.right - elif root1.right and root2.right: - queue1.append(root1.right) - queue2.append(root2.right) - return root + queue = deque() + queue.append(root1) + queue.append(root2) + + while queue: + node1 = queue.popleft() + node2 = queue.popleft() + # 更新queue + # 只有两个节点都有左节点时, 再往queue里面放. + if node1.left and node2.left: + queue.append(node1.left) + queue.append(node2.left) + # 只有两个节点都有右节点时, 再往queue里面放. + if node1.right and node2.right: + queue.append(node1.right) + queue.append(node2.right) + + # 更新当前节点. 同时改变当前节点的左右孩子. + node1.val += node2.val + if not node1.left and node2.left: + node1.left = node2.left + if not node1.right and node2.right: + node1.right = node2.right + + return root1 ``` Go: @@ -408,6 +426,46 @@ func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode { root1.Right = mergeTrees(root1.Right, root2.Right) return root1 } + +// 迭代版本 +func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode { + queue := make([]*TreeNode,0) + if root1 == nil{ + return root2 + } + if root2 == nil{ + return root1 + } + queue = append(queue,root1) + queue = append(queue,root2) + + for size:=len(queue);size>0;size=len(queue){ + node1 := queue[0] + queue = queue[1:] + node2 := queue[0] + queue = queue[1:] + node1.Val += node2.Val + // 左子树都不为空 + if node1.Left != nil && node2.Left != nil{ + queue = append(queue,node1.Left) + queue = append(queue,node2.Left) + } + // 右子树都不为空 + if node1.Right !=nil && node2.Right !=nil{ + queue = append(queue,node1.Right) + queue = append(queue,node2.Right) + } + // 树 1 的左子树为 nil,直接接上树 2 的左子树 + if node1.Left == nil{ + node1.Left = node2.Left + } + // 树 1 的右子树为 nil,直接接上树 2 的右子树 + if node1.Right == nil{ + node1.Right = node2.Right + } + } + return root1 +} ``` JavaScript: @@ -446,4 +504,4 @@ var mergeTrees = function (root1, root2) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0647.回文子串.md b/problems/0647.回文子串.md index d3b734ae..f81801b7 100644 --- a/problems/0647.回文子串.md +++ b/problems/0647.回文子串.md @@ -61,7 +61,7 @@ 以上三种情况分析完了,那么递归公式如下: -```C++ +```CPP if (s[i] == s[j]) { if (j - i <= 1) { // 情况一 和 情况二 result++; @@ -101,7 +101,7 @@ dp[i + 1][j - 1] 在 dp[i][j]的左下角,如图: 代码如下: -```C++ +```CPP for (int i = s.size() - 1; i >= 0; i--) { // 注意遍历顺序 for (int j = i; j < s.size(); j++) { if (s[i] == s[j]) { @@ -129,7 +129,7 @@ for (int i = s.size() - 1; i >= 0; i--) { // 注意遍历顺序 以上分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int countSubstrings(string s) { @@ -154,7 +154,7 @@ public: ``` 以上代码是为了凸显情况一二三,当然是可以简洁一下的,如下: -```C++ +```CPP class Solution { public: int countSubstrings(string s) { @@ -192,7 +192,7 @@ public: **这两种情况可以放在一起计算,但分别计算思路更清晰,我倾向于分别计算**,代码如下: -```C++ +```CPP class Solution { public: int countSubstrings(string s) { @@ -412,4 +412,4 @@ const countSubstrings = (s) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0649.Dota2参议院.md b/problems/0649.Dota2参议院.md index 696124d6..b73686c4 100644 --- a/problems/0649.Dota2参议院.md +++ b/problems/0649.Dota2参议院.md @@ -78,7 +78,7 @@ Dota2 参议院由来自两派的参议员组成。现在参议院希望对一 C++代码如下: -```C++ +```CPP class Solution { public: string predictPartyVictory(string senate) { @@ -136,5 +136,5 @@ public: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0654.最大二叉树.md b/problems/0654.最大二叉树.md index 4e1e7a72..8c310a21 100644 --- a/problems/0654.最大二叉树.md +++ b/problems/0654.最大二叉树.md @@ -107,7 +107,7 @@ if (maxValueIndex < (nums.size() - 1)) { ``` 这样我们就分析完了,整体代码如下:(详细注释) -```C++ +```CPP class Solution { public: TreeNode* constructMaximumBinaryTree(vector& nums) { @@ -147,7 +147,7 @@ public: 优化后代码如下: -```C++ +```CPP class Solution { private: // 在左闭右开区间[left, right),构造二叉树 @@ -182,7 +182,7 @@ public: 可以发现上面的代码看上去简洁一些,**主要是因为第二版其实是允许空节点进入递归,所以不用在递归的时候加判断节点是否为空** 第一版递归过程:(加了if判断,为了不让空节点进入递归) -```C++ +```CPP if (maxValueIndex > 0) { // 这里加了判断是为了不让空节点进入递归 vector newVec(nums.begin(), nums.begin() + maxValueIndex); @@ -354,4 +354,4 @@ var constructMaximumBinaryTree = function (nums) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0657.机器人能否返回原点.md b/problems/0657.机器人能否返回原点.md index cd26836c..480e830f 100644 --- a/problems/0657.机器人能否返回原点.md +++ b/problems/0657.机器人能否返回原点.md @@ -48,7 +48,7 @@ C++代码如下: -```C++ +```CPP class Solution { public: bool judgeCircle(string moves) { @@ -71,26 +71,89 @@ public: ## Java ```java +// 时间复杂度:O(n) +// 空间复杂度:如果采用 toCharArray,则是 O(n);如果使用 charAt,则是 O(1) +class Solution { + public boolean judgeCircle(String moves) { + int x = 0; + int y = 0; + for (char c : moves.toCharArray()) { + if (c == 'U') y++; + if (c == 'D') y--; + if (c == 'L') x++; + if (c == 'R') x--; + } + return x == 0 && y == 0; + } +} ``` ## Python ```python +# 时间复杂度:O(n) +# 空间复杂度:O(1) +class Solution: + def judgeCircle(self, moves: str) -> bool: + x = 0 # 记录当前位置 + y = 0 + for i in range(len(moves)): + if (moves[i] == 'U'): + y += 1 + if (moves[i] == 'D'): + y -= 1 + if (moves[i] == 'L'): + x += 1 + if (moves[i] == 'R'): + x -= 1 + return x == 0 and y == 0 ``` ## Go ```go +func judgeCircle(moves string) bool { + x := 0 + y := 0 + for i := 0; i < len(moves); i++ { + if moves[i] == 'U' { + y++ + } + if moves[i] == 'D' { + y-- + } + if moves[i] == 'L' { + x++ + } + if moves[i] == 'R' { + x-- + } + } + return x == 0 && y == 0; +} ``` ## JavaScript ```js +// 时间复杂度:O(n) +// 空间复杂度:O(1) +var judgeCircle = function(moves) { + var x = 0; // 记录当前位置 + var y = 0; + for (var i = 0; i < moves.length; i++) { + if (moves[i] == 'U') y++; + if (moves[i] == 'D') y--; + if (moves[i] == 'L') x++; + if (moves[i] == 'R') x--; + } + return x == 0 && y == 0; +}; ``` ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0669.修剪二叉搜索树.md b/problems/0669.修剪二叉搜索树.md index 26394eaa..267497b4 100644 --- a/problems/0669.修剪二叉搜索树.md +++ b/problems/0669.修剪二叉搜索树.md @@ -32,7 +32,7 @@ 不难写出如下代码: -```C++ +```CPP class Solution { public: TreeNode* trimBST(TreeNode* root, int low, int high) { @@ -145,7 +145,7 @@ root->left = trimBST(root->left, low, high); 最后整体代码如下: -```C++ +```CPP class Solution { public: TreeNode* trimBST(TreeNode* root, int low, int high) { @@ -167,7 +167,7 @@ public: 精简之后代码如下: -```C++ +```CPP class Solution { public: TreeNode* trimBST(TreeNode* root, int low, int high) { @@ -195,7 +195,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: TreeNode* trimBST(TreeNode* root, int L, int R) { @@ -296,6 +296,7 @@ Go: * Right *TreeNode * } */ +// 递归 func trimBST(root *TreeNode, low int, high int) *TreeNode { if root==nil{ return nil @@ -312,6 +313,37 @@ func trimBST(root *TreeNode, low int, high int) *TreeNode { root.Right=trimBST(root.Right,low,high) return root } +// 迭代 +func trimBST(root *TreeNode, low int, high int) *TreeNode { + if root == nil { + return nil + } + // 处理 root,让 root 移动到[low, high] 范围内,注意是左闭右闭 + for root != nil && (root.Valhigh){ + if root.Val < low{ + root = root.Right + }else{ + root = root.Left + } + } + cur := root + // 此时 root 已经在[low, high] 范围内,处理左孩子元素小于 low 的情况(左节点是一定小于 root.Val,因此天然小于 high) + for cur != nil{ + for cur.Left!=nil && cur.Left.Val < low{ + cur.Left = cur.Left.Right + } + cur = cur.Left + } + cur = root + // 此时 root 已经在[low, high] 范围内,处理右孩子大于 high 的情况 + for cur != nil{ + for cur.Right!=nil && cur.Right.Val > high{ + cur.Right = cur.Right.Left + } + cur = cur.Right + } + return root +} ``` @@ -373,4 +405,4 @@ var trimBST = function (root,low,high) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0673.最长递增子序列的个数.md b/problems/0673.最长递增子序列的个数.md index da0be740..ce3e8639 100644 --- a/problems/0673.最长递增子序列的个数.md +++ b/problems/0673.最长递增子序列的个数.md @@ -58,7 +58,7 @@ if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1); 代码如下: -```C++ +```CPP if (nums[i] > nums[j]) { if (dp[j] + 1 > dp[i]) { count[i] = count[j]; @@ -71,7 +71,7 @@ if (nums[i] > nums[j]) { 当然也可以这么写: -```C++ +```CPP if (nums[i] > nums[j]) { if (dp[j] + 1 > dp[i]) { dp[i] = dp[j] + 1; // 更新dp[i]放在这里,就不用max了 @@ -88,7 +88,7 @@ if (nums[i] > nums[j]) { 代码如下: -```C++ +```CPP for (int i = 1; i < nums.size(); i++) { for (int j = 0; j < i; j++) { if (nums[i] > nums[j]) { @@ -131,7 +131,7 @@ dp[i] 是由0到i-1各个位置的最长升序子序列 推导而来,那么遍 j其实就是0到i-1,遍历i的循环里外层,遍历j则在内层,代码如下: -```C++ +```CPP for (int i = 1; i < nums.size(); i++) { for (int j = 0; j < i; j++) { if (nums[i] > nums[j]) { @@ -152,7 +152,7 @@ for (int i = 1; i < nums.size(); i++) { 代码如下: -```C++ +```CPP for (int i = 1; i < nums.size(); i++) { for (int j = 0; j < i; j++) { if (nums[i] > nums[j]) { @@ -184,7 +184,7 @@ for (int i = 0; i < nums.size(); i++) { 以上分析完毕,C++整体代码如下: -```C++ +```CPP class Solution { public: int findNumberOfLIS(vector& nums) { @@ -245,5 +245,5 @@ public: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0674.最长连续递增序列.md b/problems/0674.最长连续递增序列.md index 614bff72..0fe1c49a 100644 --- a/problems/0674.最长连续递增序列.md +++ b/problems/0674.最长连续递增序列.md @@ -73,7 +73,7 @@ 本文在确定递推公式的时候也说明了为什么本题只需要一层for循环,代码如下: -```C++ +```CPP for (int i = 0; i < nums.size() - 1; i++) { if (nums[i + 1] > nums[i]) { // 连续记录 dp[i + 1] = dp[i] + 1; // 递推公式 @@ -91,7 +91,7 @@ for (int i = 0; i < nums.size() - 1; i++) { 以上分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int findLengthOfLCIS(vector& nums) { @@ -118,7 +118,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: int findLengthOfLCIS(vector& nums) { @@ -268,4 +268,4 @@ const findLengthOfLCIS = (nums) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0684.冗余连接.md b/problems/0684.冗余连接.md index 9215af5f..c9eb33c4 100644 --- a/problems/0684.冗余连接.md +++ b/problems/0684.冗余连接.md @@ -37,7 +37,7 @@ 这里整理出我的并查集模板如下: -```C++ +```CPP int n = 1005; // 节点数量3 到 1000 int father[1005]; @@ -88,7 +88,7 @@ bool same(int u, int v) { 并查集C++代码如下: -```C++ +```CPP class Solution { private: int n = 1005; // 节点数量3 到 1000 @@ -161,7 +161,6 @@ public: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
@@ -169,3 +168,4 @@ public: +
diff --git a/problems/0685.冗余连接II.md b/problems/0685.冗余连接II.md index b744c831..e282e620 100644 --- a/problems/0685.冗余连接II.md +++ b/problems/0685.冗余连接II.md @@ -58,7 +58,7 @@ 首先先计算节点的入度,代码如下: -```C++ +```CPP int inDegree[N] = {0}; // 记录节点入度 n = edges.size(); // 边的数量 for (int i = 0; i < n; i++) { @@ -70,7 +70,7 @@ for (int i = 0; i < n; i++) { 代码如下: -```C++ +```CPP vector vec; // 记录入度为2的边(如果有的话就两条边) // 找入度为2的节点所对应的边,注意要倒叙,因为优先返回最后出现在二维数组中的答案 for (int i = n - 1; i >= 0; i--) { @@ -112,7 +112,7 @@ vector getRemoveEdge(const vector>& edges) 本题C++代码如下:(详细注释了) -```C++ +```CPP class Solution { private: static const int N = 1010; // 如题:二维数组大小的在3到1000范围内 @@ -224,6 +224,6 @@ public: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0700.二叉搜索树中的搜索.md b/problems/0700.二叉搜索树中的搜索.md index d6899ac5..68d30e3f 100644 --- a/problems/0700.二叉搜索树中的搜索.md +++ b/problems/0700.二叉搜索树中的搜索.md @@ -79,7 +79,7 @@ return NULL; 整体代码如下: -```C++ +```CPP class Solution { public: TreeNode* searchBST(TreeNode* root, int val) { @@ -109,7 +109,7 @@ public: 所以迭代法代码如下: -```C++ +```CPP class Solution { public: TreeNode* searchBST(TreeNode* root, int val) { @@ -220,9 +220,18 @@ Python: # self.right = right class Solution: def searchBST(self, root: TreeNode, val: int) -> TreeNode: - if not root or root.val == val: return root //为空或者已经找到都是直接返回root,所以合并了 - if root.val > val: return self.searchBST(root.left,val) //注意一定要加return - else: return self.searchBST(root.right,val) + # 为什么要有返回值: + # 因为搜索到目标节点就要立即return, + # 这样才是找到节点就返回(搜索某一条边),如果不加return,就是遍历整棵树了。 + + if not root or root.val == val: + return root + + if root.val > val: + return self.searchBST(root.left, val) + + if root.val < val: + return self.searchBST(root.right, val) ``` @@ -355,4 +364,4 @@ var searchBST = function (root, val) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0701.二叉搜索树中的插入操作.md b/problems/0701.二叉搜索树中的插入操作.md index 61027453..fdaba43e 100644 --- a/problems/0701.二叉搜索树中的插入操作.md +++ b/problems/0701.二叉搜索树中的插入操作.md @@ -96,7 +96,7 @@ return root; 整体代码如下: -```C++ +```CPP class Solution { public: TreeNode* insertIntoBST(TreeNode* root, int val) { @@ -126,7 +126,7 @@ void traversal(TreeNode* cur, int val) 代码如下: -```C++ +```CPP class Solution { private: TreeNode* parent; @@ -172,7 +172,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: TreeNode* insertIntoBST(TreeNode* root, int val) { @@ -255,7 +255,7 @@ class Solution { Python: -递归法 +**递归法** - 有返回值 ```python class Solution: @@ -268,7 +268,63 @@ class Solution: root.left = self.insertIntoBST(root.left, val) # 递归创建左子树 return root ``` +**递归法** - 无返回值 +```python +class Solution: + def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode: + if not root: + return TreeNode(val) + parent = None + def __traverse(cur: TreeNode, val: int) -> None: + # 在函数运行的同时把新节点插入到该被插入的地方. + nonlocal parent + if not cur: + new_node = TreeNode(val) + if parent.val < val: + parent.right = new_node + else: + parent.left = new_node + return + parent = cur # 重点: parent的作用只有运行到上面if not cur:才会发挥出来. + if cur.val < val: + __traverse(cur.right, val) + else: + __traverse(cur.left, val) + return + __traverse(root, val) + return root +``` +**迭代法** +与无返回值的递归函数的思路大体一致 +```python +class Solution: + def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode: + if not root: + return TreeNode(val) + parent = None + cur = root + + # 用while循环不断地找新节点的parent + while cur: + if cur.val < val: + parent = cur + cur = cur.right + elif cur.val > val: + parent = cur + cur = cur.left + + # 运行到这意味着已经跳出上面的while循环, + # 同时意味着新节点的parent已经被找到. + # parent已被找到, 新节点已经ready. 把两个节点黏在一起就好了. + if parent.val > val: + parent.left = TreeNode(val) + else: + parent.right = TreeNode(val) + + return root + +``` Go: @@ -431,4 +487,4 @@ var insertIntoBST = function (root, val) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0704.二分查找.md b/problems/0704.二分查找.md index cce471ae..2efbd7c4 100644 --- a/problems/0704.二分查找.md +++ b/problems/0704.二分查找.md @@ -63,7 +63,7 @@ 代码如下:(详细注释) -```C++ +```CPP // 版本一 class Solution { public: @@ -102,7 +102,7 @@ public: 代码如下:(详细注释) -```C++ +```CPP // 版本二 class Solution { public: @@ -235,8 +235,6 @@ class Solution: return -1 ``` - - **Go:** (版本一)左闭右闭区间 @@ -279,7 +277,7 @@ func search(nums []int, target int) int { } ``` -**javaScript:** +**JavaScript:** ```js @@ -316,7 +314,97 @@ var search = function(nums, target) { ``` +**Ruby:** +```ruby +# (版本一)左闭右闭区间 + +def search(nums, target) + left, right = 0, nums.length - 1 + while left <= right # 由于定义target在一个在左闭右闭的区间里,因此极限情况下存在left==right + middle = (left + right) / 2 + if nums[middle] > target + right = middle - 1 + elsif nums[middle] < target + left = middle + 1 + else + return middle # return兼具返回与跳出循环的作用 + end + end + -1 +end + +# (版本二)左闭右开区间 + +def search(nums, target) + left, right = 0, nums.length + while left < right # 由于定义target在一个在左闭右开的区间里,因此极限情况下right=left+1 + middle = (left + right) / 2 + if nums[middle] > target + right = middle + elsif nums[middle] < target + left = middle + 1 + else + return middle + end + end + -1 +end +``` + +**Swift:** + +```swift +// (版本一)左闭右闭区间 +func search(nums: [Int], target: Int) -> Int { + // 1. 先定义区间。这里的区间是[left, right] + var left = 0 + var right = nums.count - 1 + + while left <= right {// 因为taeget是在[left, right]中,包括两个边界值,所以这里的left == right是有意义的 + // 2. 计算区间中间的下标(如果left、right都比较大的情况下,left + right就有可能会溢出) + // let middle = (left + right) / 2 + // 防溢出: + let middle = left + (right - left) / 2 + + // 3. 判断 + if target < nums[middle] { + // 当目标在区间左侧,就需要更新右边的边界值,新区间为[left, middle - 1] + right = middle - 1 + } else if target > nums[middle] { + // 当目标在区间右侧,就需要更新左边的边界值,新区间为[middle + 1, right] + left = middle + 1 + } else { + // 当目标就是在中间,则返回中间值的下标 + return middle + } + } + + // 如果找不到目标,则返回-1 + return -1 +} + +// (版本二)左闭右开区间 +func search(nums: [Int], target: Int) -> Int { + var left = 0 + var right = nums.count + + while left < right { + let middle = left + ((right - left) >> 1) + + if target < nums[middle] { + right = middle + } else if target > nums[middle] { + left = middle + 1 + } else { + return middle + } + } + + return -1 +} + +``` @@ -324,4 +412,4 @@ var search = function(nums, target) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0707.设计链表.md b/problems/0707.设计链表.md index 8be28f88..0aa038e8 100644 --- a/problems/0707.设计链表.md +++ b/problems/0707.设计链表.md @@ -56,7 +56,7 @@ https://leetcode-cn.com/problems/design-linked-list/ ## 代码 -```C++ +```CPP class MyLinkedList { public: // 定义链表节点结构体 @@ -154,7 +154,129 @@ private: ## 其他语言版本 +C: +```C +typedef struct { + int val; + struct MyLinkedList* next; +}MyLinkedList; +/** Initialize your data structure here. */ + +MyLinkedList* myLinkedListCreate() { + //这个题必须用虚拟头指针,参数都是一级指针,头节点确定后没法改指向了!!! + MyLinkedList* head = (MyLinkedList *)malloc(sizeof (MyLinkedList)); + head->next = NULL; + return head; +} + +/** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */ +int myLinkedListGet(MyLinkedList* obj, int index) { + MyLinkedList *cur = obj->next; + for (int i = 0; cur != NULL; i++){ + if (i == index){ + return cur->val; + } + else{ + cur = cur->next; + } + } + return -1; +} + +/** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */ +void myLinkedListAddAtHead(MyLinkedList* obj, int val) { + MyLinkedList *nhead = (MyLinkedList *)malloc(sizeof (MyLinkedList)); + nhead->val = val; + nhead->next = obj->next; + obj->next = nhead; + +} + +/** Append a node of value val to the last element of the linked list. */ +void myLinkedListAddAtTail(MyLinkedList* obj, int val) { + MyLinkedList *cur = obj; + while(cur->next != NULL){ + cur = cur->next; + } + MyLinkedList *ntail = (MyLinkedList *)malloc(sizeof (MyLinkedList)); + ntail->val = val; + ntail->next = NULL; + cur->next = ntail; +} + +/** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */ +void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) { + if (index == 0){ + myLinkedListAddAtHead(obj, val); + return; + } + MyLinkedList *cur = obj->next; + for (int i = 1 ;cur != NULL; i++){ + if (i == index){ + MyLinkedList* newnode = (MyLinkedList *)malloc(sizeof (MyLinkedList)); + newnode->val = val; + newnode->next = cur->next; + cur->next = newnode; + return; + } + else{ + cur = cur->next; + } + } +} + +/** Delete the index-th node in the linked list, if the index is valid. */ +void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) { + if (index == 0){ + MyLinkedList *tmp = obj->next; + if (tmp != NULL){ + obj->next = tmp->next; + free(tmp) + } + return; + } + MyLinkedList *cur = obj->next; + for (int i = 1 ;cur != NULL && cur->next != NULL; i++){ + if (i == index){ + MyLinkedList *tmp = cur->next; + if (tmp != NULL) { + cur->next = tmp->next; + free(tmp); + } + return; + } + else{ + cur = cur->next; + } + } + +} + +void myLinkedListFree(MyLinkedList* obj) { + while(obj != NULL){ + MyLinkedList *tmp = obj; + obj = obj->next; + free(tmp); + } +} + +/** + * Your MyLinkedList struct will be instantiated and called as such: + * MyLinkedList* obj = myLinkedListCreate(); + * int param_1 = myLinkedListGet(obj, index); + + * myLinkedListAddAtHead(obj, val); + + * myLinkedListAddAtTail(obj, val); + + * myLinkedListAddAtIndex(obj, index, val); + + * myLinkedListDeleteAtIndex(obj, index); + + * myLinkedListFree(obj); +*/ +``` Java: ```Java @@ -758,7 +880,73 @@ MyLinkedList.prototype.deleteAtIndex = function(index) { * obj.deleteAtIndex(index) */ ``` +Kotlin: +```kotlin +class MyLinkedList { + var next: ListNode? = null + + var size: Int = 0 + + fun get(index: Int): Int { + if (index + 1 > size) return -1 + var cur = this.next + for (i in 0 until index) { + cur = cur?.next + } + return cur?.`val` ?: -1 + } + + fun addAtHead(`val`: Int) { + val head = ListNode(`val`) + head.next = this.next + this.next = head + size++ + } + + fun addAtTail(`val`: Int) { + val pre = ListNode(0) + pre.next = this.next + var cur: ListNode? = pre + while (cur?.next != null) { + cur = cur.next + } + cur?.next = ListNode(`val`) + this.next = pre.next + size++ + } + + fun addAtIndex(index: Int, `val`: Int) { + if (index > size) return + val pre = ListNode(0) + pre.next = this.next + var cur:ListNode? = pre + for (i in 0 until index) { + cur = cur?.next + } + val temp = cur?.next + cur?.next = ListNode(`val`) + cur?.next?.next = temp + this.next = pre.next + size++ + } + + fun deleteAtIndex(index: Int) { + if (index + 1 > size) return + val pre = ListNode(0) + pre.next = this.next + var cur: ListNode? = pre + for (i in 0 until index) { + cur = cur?.next + } + val temp = cur?.next?.next + cur?.next?.next = null + cur?.next = temp + this.next = pre.next + size-- + } +} +``` @@ -766,4 +954,4 @@ MyLinkedList.prototype.deleteAtIndex = function(index) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0714.买卖股票的最佳时机含手续费.md b/problems/0714.买卖股票的最佳时机含手续费.md index 2e8f9208..b0a64bd2 100644 --- a/problems/0714.买卖股票的最佳时机含手续费.md +++ b/problems/0714.买卖股票的最佳时机含手续费.md @@ -60,7 +60,7 @@ 贪心算法C++代码如下: -```C++ +```CPP class Solution { public: int maxProfit(vector& prices, int fee) { @@ -101,7 +101,7 @@ public: C++代码如下: -```C++ +```CPP class Solution { public: int maxProfit(vector& prices, int fee) { @@ -126,7 +126,7 @@ public: C++ 代码如下: -```C++ +```CPP class Solution { public: int maxProfit(vector& prices, int fee) { @@ -216,7 +216,30 @@ class Solution: # 贪心思路 ``` Go: - +```golang +func maxProfit(prices []int, fee int) int { + var minBuy int = prices[0] //第一天买入 + var res int + for i:=0;i=minBuy&&prices[i]-fee-minBuy<=0{ + continue + } + //可以售卖了 + if prices[i]>minBuy+fee{ + //累加每天的收益 + res+=prices[i]-minBuy-fee + //更新最小值(如果还在收获利润的区间里,表示并不是真正的卖出,而计算利润每次都要减去手续费,所以要让minBuy = prices[i] - fee;,这样在明天收获利润的时候,才不会多减一次手续费!) + minBuy=prices[i]-fee + } + } + return res +} +``` Javascript: ```Javascript // 贪心思路 @@ -246,4 +269,4 @@ var maxProfit = function(prices, fee) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0714.买卖股票的最佳时机含手续费(动态规划).md b/problems/0714.买卖股票的最佳时机含手续费(动态规划).md index e2b83999..53952a7f 100644 --- a/problems/0714.买卖股票的最佳时机含手续费(动态规划).md +++ b/problems/0714.买卖股票的最佳时机含手续费(动态规划).md @@ -72,7 +72,7 @@ dp[i][1] 表示第i天不持有股票所得最多现金 以上分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int maxProfit(vector& prices, int fee) { @@ -171,4 +171,4 @@ const maxProfit = (prices,fee) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0718.最长重复子数组.md b/problems/0718.最长重复子数组.md index 9ee94a3d..2fe38f0a 100644 --- a/problems/0718.最长重复子数组.md +++ b/problems/0718.最长重复子数组.md @@ -74,7 +74,7 @@ dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最 代码如下: -```C++ +```CPP for (int i = 1; i <= A.size(); i++) { for (int j = 1; j <= B.size(); j++) { if (A[i - 1] == B[j - 1]) { @@ -94,7 +94,7 @@ for (int i = 1; i <= A.size(); i++) { 以上五部曲分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int findLength(vector& A, vector& B) { @@ -128,7 +128,7 @@ public: **此时遍历B数组的时候,就要从后向前遍历,这样避免重复覆盖**。 -```C++ +```CPP class Solution { public: int findLength(vector& A, vector& B) { @@ -252,10 +252,37 @@ func findLength(A []int, B []int) int { } ``` +JavaScript: + +> 动态规划 + +```javascript +const findLength = (A, B) => { + // A、B数组的长度 + const [m, n] = [A.length, B.length]; + // dp数组初始化,都初始化为0 + const dp = new Array(m + 1).fill(0).map(x => new Array(n + 1).fill(0)); + // 初始化最大长度为0 + let res = 0; + for (let i = 1; i <= m; i++) { + for (let j = 1; j <= n; j++) { + // 遇到A[i - 1] === B[j - 1],则更新dp数组 + if (A[i - 1] === B[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } + // 更新res + res = dp[i][j] > res ? dp[i][j] : res; + } + } + // 遍历完成,返回res + return res; +}; +``` + ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0724.寻找数组的中心索引.md b/problems/0724.寻找数组的中心索引.md index bf989979..b4115893 100644 --- a/problems/0724.寻找数组的中心索引.md +++ b/problems/0724.寻找数组的中心索引.md @@ -43,7 +43,7 @@ * 判断leftSum和rightSum是否相同 C++代码如下: -```C++ +```CPP class Solution { public: int pivotIndex(vector& nums) { @@ -67,11 +67,38 @@ public: ## Java ```java +class Solution { + public int pivotIndex(int[] nums) { + int sum = 0; + for (int i = 0; i < nums.length; i++) { + sum += nums[i]; // 总和 + } + int leftSum = 0; + int rightSum = 0; + for (int i = 0; i < nums.length; i++) { + leftSum += nums[i]; + rightSum = sum - leftSum + nums[i]; // leftSum 里面已经有 nums[i],多减了一次,所以加上 + if (leftSum == rightSum) { + return i; + } + } + return -1; // 不存在 + } +} ``` ## Python -```python +```python3 +class Solution: + def pivotIndex(self, nums: List[int]) -> int: + numSum = sum(nums) #数组总和 + leftSum = 0 + for i in range(len(nums)): + if numSum - leftSum -nums[i] == leftSum: #左右和相等 + return i + leftSum += nums[i] + return -1 ``` ## Go @@ -88,6 +115,5 @@ public: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
- +
diff --git a/problems/0738.单调递增的数字.md b/problems/0738.单调递增的数字.md index 75c7f250..82f7f3ae 100644 --- a/problems/0738.单调递增的数字.md +++ b/problems/0738.单调递增的数字.md @@ -34,7 +34,7 @@ 题意很简单,那么首先想的就是暴力解法了,来我替大家暴力一波,结果自然是超时! 代码如下: -```C++ +```CPP class Solution { private: bool checkNum(int num) { @@ -87,7 +87,7 @@ public: C++代码如下: -```C++ +```CPP class Solution { public: int monotoneIncreasingDigits(int N) { @@ -159,7 +159,26 @@ class Solution: ``` Go: - +```golang +func monotoneIncreasingDigits(N int) int { + s := strconv.Itoa(N)//将数字转为字符串,方便使用下标 + ss := []byte(s)//将字符串转为byte数组,方便更改。 + n := len(ss) + if n <= 1 { + return N + } + for i:=n-1 ; i>0; i-- { + if ss[i-1] > ss[i] {//前一个大于后一位,前一位减1,后面的全部置为9 + ss[i-1] -= 1 + for j := i ; j < n; j++ {//后面的全部置为9 + ss[j] = '9' + } + } + } + res, _ := strconv.Atoi(string(ss)) + return res +} +``` Javascript: ```Javascript var monotoneIncreasingDigits = function(n) { @@ -190,4 +209,4 @@ var monotoneIncreasingDigits = function(n) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0739.每日温度.md b/problems/0739.每日温度.md index 8ad79fe3..ddcbe428 100644 --- a/problems/0739.每日温度.md +++ b/problems/0739.每日温度.md @@ -117,7 +117,7 @@ T[4]弹出之后, T[5] > T[3] (当前遍历的元素T[i]大于栈顶元素T[ C++代码如下: -```C++ +```CPP // 版本一 class Solution { public: @@ -148,7 +148,7 @@ public: 精简代码如下: -```C++ +```CPP // 版本二 class Solution { public: @@ -283,5 +283,5 @@ func dailyTemperatures(num []int) []int { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0746.使用最小花费爬楼梯.md b/problems/0746.使用最小花费爬楼梯.md index 4238a389..9b691c5f 100644 --- a/problems/0746.使用最小花费爬楼梯.md +++ b/problems/0746.使用最小花费爬楼梯.md @@ -98,7 +98,7 @@ dp[1] = cost[1]; 以上分析完毕,整体C++代码如下: -```C++ +```CPP // 版本一 class Solution { public: @@ -120,7 +120,7 @@ public: 还可以优化空间复杂度,因为dp[i]就是由前两位推出来的,那么也不用dp数组了,C++代码如下: -```C++ +```CPP // 版本二 class Solution { public: @@ -165,7 +165,7 @@ public: 所以代码这么写: -```C++ +```CPP class Solution { public: int minCostClimbingStairs(vector& cost) { @@ -272,4 +272,4 @@ var minCostClimbingStairs = function(cost) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0763.划分字母区间.md b/problems/0763.划分字母区间.md index b36e00b7..ccf654d4 100644 --- a/problems/0763.划分字母区间.md +++ b/problems/0763.划分字母区间.md @@ -47,7 +47,7 @@ 明白原理之后,代码并不复杂,如下: -```C++ +```CPP class Solution { public: vector partitionLabels(string S) { @@ -181,4 +181,4 @@ var partitionLabels = function(s) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0841.钥匙和房间.md b/problems/0841.钥匙和房间.md index 1b3c4a95..bd7c99c9 100644 --- a/problems/0841.钥匙和房间.md +++ b/problems/0841.钥匙和房间.md @@ -54,7 +54,7 @@ BFS C++代码代码如下: -```C++ +```CPP class Solution { bool bfs(const vector>& rooms) { vector visited(rooms.size(), 0); // 标记房间是否被访问过 @@ -89,7 +89,7 @@ public: DFS C++代码如下: -```C++ +```CPP class Solution { private: void dfs(int key, const vector>& rooms, vector& visited) { @@ -131,5 +131,5 @@ JavaScript: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0844.比较含退格的字符串.md b/problems/0844.比较含退格的字符串.md index 9f37959d..32232c4a 100644 --- a/problems/0844.比较含退格的字符串.md +++ b/problems/0844.比较含退格的字符串.md @@ -14,7 +14,7 @@ 给定 S 和 T 两个字符串,当它们分别被输入到空白的文本编辑器后,判断二者是否相等,并返回结果。 # 代表退格字符。 注意:如果对空文本输入退格字符,文本继续为空。 -  + 示例 1: * 输入:S = "ab#c", T = "ad#c" * 输出:true @@ -50,7 +50,7 @@ 代码如下: -```C++ +```CPP class Solution { public: bool backspaceCompare(string S, string T) { @@ -78,7 +78,7 @@ public: 当然以上代码,大家可以发现有重复的逻辑处理S,处理T,可以把这块公共逻辑抽离出来,代码精简如下: -```C++ +```CPP class Solution { private: string getString(const string& S) { @@ -115,7 +115,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: bool backspaceCompare(string S, string T) { @@ -160,6 +160,32 @@ public: Java: +```java +// 普通方法(使用栈的思路) +class Solution { + public boolean backspaceCompare(String s, String t) { + StringBuilder ssb = new StringBuilder(); // 模拟栈 + StringBuilder tsb = new StringBuilder(); // 模拟栈 + // 分别处理两个 String + for (char c : s.toCharArray()) { + if (c != '#') { + ssb.append(c); // 模拟入栈 + } else if (ssb.length() > 0){ // 栈非空才能弹栈 + ssb.deleteCharAt(ssb.length() - 1); // 模拟弹栈 + } + } + for (char c : t.toCharArray()) { + if (c != '#') { + tsb.append(c); // 模拟入栈 + } else if (tsb.length() > 0){ // 栈非空才能弹栈 + tsb.deleteCharAt(tsb.length() - 1); // 模拟弹栈 + } + } + return ssb.toString().equals(tsb.toString()); + } +} +``` + Python: Go: @@ -170,5 +196,5 @@ JavaScript: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0860.柠檬水找零.md b/problems/0860.柠檬水找零.md index 0d5a7075..792bee9a 100644 --- a/problems/0860.柠檬水找零.md +++ b/problems/0860.柠檬水找零.md @@ -82,7 +82,7 @@ C++代码如下: -```C++ +```CPP class Solution { public: bool lemonadeChange(vector& bills) { @@ -260,4 +260,4 @@ var lemonadeChange = function(bills) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0922.按奇偶排序数组II.md b/problems/0922.按奇偶排序数组II.md index bb609c12..97d7091e 100644 --- a/problems/0922.按奇偶排序数组II.md +++ b/problems/0922.按奇偶排序数组II.md @@ -32,7 +32,7 @@ 其实这道题可以用很朴实的方法,时间复杂度就就是O(n)了,C++代码如下: -```C++ +```CPP class Solution { public: vector sortArrayByParityII(vector& A) { @@ -64,7 +64,7 @@ public: 以上代码我是建了两个辅助数组,而且A数组还相当于遍历了两次,用辅助数组的好处就是思路清晰,优化一下就是不用这两个辅助树,代码如下: -```C++ +```CPP class Solution { public: vector sortArrayByParityII(vector& A) { @@ -93,7 +93,7 @@ public: 当然还可以在原数组上修改,连result数组都不用了。 -```C++ +```CPP class Solution { public: vector sortArrayByParityII(vector& A) { @@ -120,11 +120,61 @@ public: ## Java ```java +// 方法一 +class Solution { + public int[] sortArrayByParityII(int[] nums) { + // 分别存放 nums 中的奇数、偶数 + int len = nums.length; + int evenIndex = 0; + int oddIndex = 0; + int[] even = new int[len / 2]; + int[] odd = new int[len / 2]; + for (int i = 0; i < len; i++) { + if (nums[i] % 2 == 0) { + even[evenIndex++] = nums[i]; + } else { + odd[oddIndex++] = nums[i]; + } + } + // 把奇偶数组重新存回 nums + int index = 0; + for (int i = 0; i < even.length; i++) { + nums[index++] = even[i]; + nums[index++] = odd[i]; + } + return nums; + } +} ``` ## Python -```python +```python3 +#方法2 +class Solution: + def sortArrayByParityII(self, nums: List[int]) -> List[int]: + result = [0]*len(nums) + evenIndex = 0 + oddIndex = 1 + for i in range(len(nums)): + if nums[i] % 2: #奇数 + result[oddIndex] = nums[i] + oddIndex += 2 + else: #偶数 + result[evenIndex] = nums[i] + evenIndex += 2 + return result + +#方法3 +class Solution: + def sortArrayByParityII(self, nums: List[int]) -> List[int]: + oddIndex = 1 + for i in range(0,len(nums),2): #步长为2 + if nums[i] % 2: #偶数位遇到奇数 + while nums[oddIndex] % 2: #奇数位找偶数 + oddIndex += 2 + nums[i], nums[oddIndex] = nums[oddIndex], nums[i] + return nums ``` ## Go @@ -141,6 +191,5 @@ public: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
- +
diff --git a/problems/0925.长按键入.md b/problems/0925.长按键入.md index 95b6325a..c851a8df 100644 --- a/problems/0925.长按键入.md +++ b/problems/0925.长按键入.md @@ -8,6 +8,7 @@

欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

# 925.长按键入 +题目链接:https://leetcode-cn.com/problems/long-pressed-name/ 你的朋友正在使用键盘输入他的名字 name。偶尔,在键入字符 c 时,按键可能会被长按,而字符可能被输入 1 次或多次。 @@ -59,7 +60,7 @@ 上面的逻辑想清楚了,不难写出如下C++代码: -```C++ +```CPP class Solution { public: bool isLongPressedName(string name, string typed) { @@ -98,9 +99,62 @@ public: # 其他语言版本 Java: - +```java +class Solution { + public boolean isLongPressedName(String name, String typed) { + int i = 0, j = 0; + int m = name.length(), n = typed.length(); + while (i< m && j < n) { + if (name.charAt(i) == typed.charAt(j)) { // 相同则同时向后匹配 + i++; j++; + } + else { + if (j == 0) return false; // 如果是第一位就不相同直接返回false + // 判断边界为n-1,若为n会越界,例如name:"kikcxmvzi" typed:"kiikcxxmmvvzzz" + while (j < n-1 && typed.charAt(j) == typed.charAt(j-1)) j++; + if (name.charAt(i) == typed.charAt(j)) { // j跨越重复项之后再次和name[i]匹配 + i++; j++; // 相同则同时向后匹配 + } + else return false; + } + } + // 说明name没有匹配完 + if (i < m) return false; + // 说明type没有匹配完 + while (j < n) { + if (typed.charAt(j) == typed.charAt(j-1)) j++; + else return false; + } + return true; + } +} +``` Python: - +```python +class Solution: + def isLongPressedName(self, name: str, typed: str) -> bool: + i, j = 0, 0 + m, n = len(name) , len(typed) + while i< m and j < n: + if name[i] == typed[j]: # 相同时向后匹配 + i += 1 + j += 1 + else: # 不相同 + if j == 0: return False # 如果第一位不相同,直接返回false + # 判断边界为n-1,若为n会越界,例如name:"kikcxmvzi" typed:"kiikcxxmmvvzzz" + while j < n - 1 and typed[j] == typed[j-1]: j += 1 + if name[i] == typed[j]: + i += 1 + j += 1 + else: return False + # 说明name没有匹配完 + if i < m: return False + # 说明type没有匹配完 + while j < n: + if typed[j] == typed[j-1]: j += 1 + else: return False + return True +``` Go: JavaScript: @@ -109,5 +163,5 @@ JavaScript: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0941.有效的山脉数组.md b/problems/0941.有效的山脉数组.md index 6dbc3da2..98344949 100644 --- a/problems/0941.有效的山脉数组.md +++ b/problems/0941.有效的山脉数组.md @@ -1,9 +1,41 @@ -## 题目链接 +

+ + + + +

+

欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

-https://leetcode-cn.com/problems/valid-mountain-array/ +# 941.有效的山脉数组 -## 思路 +题目链接:https://leetcode-cn.com/problems/valid-mountain-array/ + +给定一个整数数组 arr,如果它是有效的山脉数组就返回 true,否则返回 false。 + +让我们回顾一下,如果 A 满足下述条件,那么它是一个山脉数组: + +* arr.length >= 3 +* 在 0 < i < arr.length - 1 条件下,存在 i 使得: + * arr[0] < arr[1] < ... arr[i-1] < arr[i] + * arr[i] > arr[i+1] > ... > arr[arr.length - 1] + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20210729103604.png) + +示例 1: +* 输入:arr = [2,1] +* 输出:false + +示例 2: +* 输入:arr = [3,5,5] +* 输出:false + +示例 3: +* 输入:arr = [0,3,2,1] +* 输出:true + + +# 思路 判断是山峰,主要就是要严格的保存左边到中间,和右边到中间是递增的。 @@ -18,7 +50,7 @@ https://leetcode-cn.com/problems/valid-mountain-array/ C++代码如下: -``` +```c++ class Solution { public: bool validMountainArray(vector& A) { @@ -38,6 +70,59 @@ public: } }; ``` + 如果想系统学一学双指针的话, 可以看一下这篇[双指针法:总结篇!](https://mp.weixin.qq.com/s/_p7grwjISfMh0U65uOyCjA) +# 其他语言版本 +## Java + +```java +class Solution { + public boolean validMountainArray(int[] arr) { + if (arr.length < 3) { // 此时,一定不是有效的山脉数组 + return false; + } + // 双指针 + int left = 0; + int right = arr.length - 1; + // 注意防止指针越界 + while (left + 1 < arr.length && arr[left] < arr[left + 1]) { + left++; + } + // 注意防止指针越界 + while (right > 0 && arr[right] < arr[right - 1]) { + right--; + } + // 如果left或者right都在起始位置,说明不是山峰 + if (left == right && left != 0 && right != arr.length - 1) { + return true; + } + return false; + } +} +``` + +## Python + +```python +``` + +## Go + +```go +``` + +## JavaScript + +```js +``` + +----------------------- +* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) +* B站视频:[代码随想录](https://space.bilibili.com/525438321) +* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) + + + +
diff --git a/problems/0968.监控二叉树.md b/problems/0968.监控二叉树.md index a0eb5883..380ca8f8 100644 --- a/problems/0968.监控二叉树.md +++ b/problems/0968.监控二叉树.md @@ -216,7 +216,7 @@ int minCameraCover(TreeNode* root) { ## C++代码 -```C++ +```CPP // 版本一 class Solution { private: @@ -270,7 +270,7 @@ public: 在以上代码的基础上,再进行精简,代码如下: -```C++ +```CPP // 版本二 class Solution { private: @@ -411,4 +411,4 @@ var minCameraCover = function(root) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/0977.有序数组的平方.md b/problems/0977.有序数组的平方.md index 0f9007d7..71c46401 100644 --- a/problems/0977.有序数组的平方.md +++ b/problems/0977.有序数组的平方.md @@ -29,7 +29,7 @@ https://leetcode-cn.com/problems/squares-of-a-sorted-array/ 最直观的相反,莫过于:每个数平方之后,排个序,美滋滋,代码如下: -```C++ +```CPP class Solution { public: vector sortedSquares(vector& A) { @@ -64,7 +64,7 @@ public: 不难写出如下代码: -```C++ +```CPP class Solution { public: vector sortedSquares(vector& A) { @@ -224,8 +224,56 @@ const sortedSquares = function (nums) { } ``` +Swift: + +```swift +func sortedSquares(_ nums: [Int]) -> [Int] { + // 指向新数组最后一个元素 + var k = nums.count - 1 + // 指向原数组第一个元素 + var i = 0 + // 指向原数组最后一个元素 + var j = nums.count - 1 + // 初始化新数组(用-1填充) + var result = Array(repeating: -1, count: nums.count) + + for _ in 0.. nums[right]**2 + result << nums[left]**2 + left += 1 + else + result << nums[right]**2 + right -= 1 + end + end + result.reverse +end +``` + + + ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/1002.查找常用字符.md b/problems/1002.查找常用字符.md index 875340c4..723e5656 100644 --- a/problems/1002.查找常用字符.md +++ b/problems/1002.查找常用字符.md @@ -97,7 +97,7 @@ for (int i = 0; i < 26; i++) { 整体C++代码如下: -```C++ +```CPP class Solution { public: vector commonChars(vector& A) { @@ -169,9 +169,73 @@ class Solution { } } ``` +Python +```python +class Solution: + def commonChars(self, words: List[str]) -> List[str]: + if not words: return [] + result = [] + hash = [0] * 26 # 用来统计所有字符串里字符出现的最小频率 + for i, c in enumerate(words[0]): # 用第一个字符串给hash初始化 + hash[ord(c) - ord('a')] += 1 + # 统计除第一个字符串外字符的出现频率 + for i in range(1, len(words)): + hashOtherStr = [0] * 26 + for j in range(len(words[0])): + hashOtherStr[ord(words[i][j]) - ord('a')] += 1 + # 更新hash,保证hash里统计26个字符在所有字符串里出现的最小次数 + for k in range(26): + hash[k] = min(hash[k], hashOtherStr[k]) + # 将hash统计的字符次数,转成输出形式 + for i in range(26): + while hash[i] != 0: # 注意这里是while,多个重复的字符 + result.extend(chr(i + ord('a'))) + hash[i] -= 1 + return result +``` +javaScript +```js +var commonChars = function (words) { + let res = [] + let size = 26 + let firstHash = new Array(size) + for (let i = 0; i < size; i++) { // 初始化 hash 数组 + firstHash[i] = 0 + } + + let a = "a".charCodeAt() + let firstWord = words[0] + for (let i = 0; i < firstWord.length; i++) { // 第 0 个单词的统计 + let idx = firstWord[i].charCodeAt() + firstHash[idx - a] += 1 + } + + for (let i = 1; i < words.length; i++) { // 1-n 个单词统计 + let otherHash = new Array(size) + for (let i = 0; i < size; i++) { // 初始化 hash 数组 + otherHash[i] = 0 + } + + for (let j = 0; j < words[i].length; j++) { + let idx = words[i][j].charCodeAt() + otherHash[idx - a] += 1 + } + for (let i = 0; i < size; i++) { + firstHash[i] = Math.min(firstHash[i], otherHash[i]) + } + } + for (let i = 0; i < size; i++) { + while (firstHash[i] > 0) { + res.push(String.fromCharCode(i + a)) + firstHash[i]-- + } + } + return res +}; +``` ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/1005.K次取反后最大化的数组和.md b/problems/1005.K次取反后最大化的数组和.md index 3534e1e2..a689fbf7 100644 --- a/problems/1005.K次取反后最大化的数组和.md +++ b/problems/1005.K次取反后最大化的数组和.md @@ -61,7 +61,7 @@ 对应C++代码如下: -```C++ +```CPP class Solution { static bool cmp(int a, int b) { return abs(a) > abs(b); @@ -219,4 +219,4 @@ var largestSumAfterKNegations = function(nums, k) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/1035.不相交的线.md b/problems/1035.不相交的线.md index cb74ef75..3172cfc7 100644 --- a/problems/1035.不相交的线.md +++ b/problems/1035.不相交的线.md @@ -45,7 +45,7 @@ 本题代码如下: -```C++ +```CPP class Solution { public: int maxUncrossedLines(vector& A, vector& B) { @@ -109,7 +109,28 @@ class Solution: return dp[-1][-1] ``` -Go: +JavaScript: + +```javascript +const maxUncrossedLines = (nums1, nums2) => { + // 两个数组长度 + const [m, n] = [nums1.length, nums2.length]; + // 创建dp数组并都初始化为0 + const dp = new Array(m + 1).fill(0).map(x => new Array(n + 1).fill(0)); + for (let i = 1; i <= m; i++) { + for (let j = 1; j <= n; j++) { + // 根据两种情况更新dp[i][j] + if (nums1[i - 1] === nums2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + } + } + } + // 返回dp数组中右下角的元素 + return dp[m][n]; +}; +``` @@ -118,4 +139,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/1047.删除字符串中的所有相邻重复项.md b/problems/1047.删除字符串中的所有相邻重复项.md index c6a49376..c4ae85c9 100644 --- a/problems/1047.删除字符串中的所有相邻重复项.md +++ b/problems/1047.删除字符串中的所有相邻重复项.md @@ -69,7 +69,7 @@ https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string/ C++代码 : -```C++ +```CPP class Solution { public: string removeDuplicates(string S) { @@ -97,7 +97,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: string removeDuplicates(string S) { @@ -252,4 +252,4 @@ var removeDuplicates = function(s) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/1049.最后一块石头的重量II.md b/problems/1049.最后一块石头的重量II.md index c09e476a..7b71eae4 100644 --- a/problems/1049.最后一块石头的重量II.md +++ b/problems/1049.最后一块石头的重量II.md @@ -93,7 +93,7 @@ vector dp(15001, 0); 代码如下: -```C++ +```CPP for (int i = 0; i < stones.size(); i++) { // 遍历物品 for (int j = target; j >= stones[i]; j--) { // 遍历背包 dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]); @@ -119,7 +119,7 @@ for (int i = 0; i < stones.size(); i++) { // 遍历物品 以上分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int lastStoneWeightII(vector& stones) { @@ -225,4 +225,4 @@ func max(a, b int) int { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/1143.最长公共子序列.md b/problems/1143.最长公共子序列.md index e1fc1abb..c0468d08 100644 --- a/problems/1143.最长公共子序列.md +++ b/problems/1143.最长公共子序列.md @@ -65,7 +65,7 @@ dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符 代码如下: -```C++ +```CPP if (text1[i - 1] == text2[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { @@ -107,7 +107,7 @@ vector> dp(text1.size() + 1, vector(text2.size() + 1, 0)); 以上分析完毕,C++代码如下: -```C++ +```CPP class Solution { public: int longestCommonSubsequence(string text1, string text2) { @@ -221,4 +221,4 @@ const longestCommonSubsequence = (text1, text2) => { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/1207.独一无二的出现次数.md b/problems/1207.独一无二的出现次数.md index 377d9ebf..1c09ee8a 100644 --- a/problems/1207.独一无二的出现次数.md +++ b/problems/1207.独一无二的出现次数.md @@ -53,7 +53,7 @@ C++代码如下: -```C++ +```CPP class Solution { public: bool uniqueOccurrences(vector& arr) { @@ -77,8 +77,44 @@ public: Java: -Python: +```java +class Solution { + public boolean uniqueOccurrences(int[] arr) { + int[] count = new int[2002]; + for (int i = 0; i < arr.length; i++) { + count[arr[i] + 1000]++; // 防止负数作为下标 + } + boolean[] flag = new boolean[1002]; // 标记相同频率是否重复出现 + for (int i = 0; i <= 2000; i++) { + if (count[i] > 0) { + if (flag[count[i]] == false) { + flag[count[i]] = true; + } else { + return false; + } + } + } + return true; + } +} +``` +Python: +```python +class Solution: + def uniqueOccurrences(self, arr: List[int]) -> bool: + count = [0] * 2002 + for i in range(len(arr)): + count[arr[i] + 1000] += 1 # 防止负数作为下标 + freq = [False] * 1002 # 标记相同频率是否重复出现 + for i in range(2001): + if count[i] > 0: + if freq[count[i]] == False: + freq[count[i]] = True + else: + return False + return True +``` Go: JavaScript: @@ -87,6 +123,5 @@ JavaScript: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
- +
diff --git a/problems/1356.根据数字二进制下1的数目排序.md b/problems/1356.根据数字二进制下1的数目排序.md index e464f716..300aa0e2 100644 --- a/problems/1356.根据数字二进制下1的数目排序.md +++ b/problems/1356.根据数字二进制下1的数目排序.md @@ -16,7 +16,7 @@ 换而言之,对于每个 nums[i] 你必须计算出有效的 j 的数量,其中 j 满足 j != i 且 nums[j] < nums[i] 。 以数组形式返回答案。 -  + 示例 1: 输入:nums = [8,1,2,2,3] @@ -35,7 +35,7 @@ 示例 3: 输入:nums = [7,7,7,7] 输出:[0,0,0,0] -  + 提示: * 2 <= nums.length <= 500 * 0 <= nums[i] <= 100 @@ -68,7 +68,7 @@ sort(vec.begin(), vec.end()); // 从小到大排序之后,元素下标就是 这里就需要一个技巧了,**在构造数组hash的时候,从后向前遍历,这样hash里存放的就是相同元素最左面的数值和下标了**。 代码如下: -```C++ +```CPP int hash[101]; for (int i = vec.size() - 1; i >= 0; i--) { // 从后向前,记录 vec[i] 对应的下标 hash[vec[i]] = i; @@ -79,7 +79,7 @@ for (int i = vec.size() - 1; i >= 0; i--) { // 从后向前,记录 vec[i] 对 代码如下: -```C++ +```CPP // 此时hash里保存的每一个元素数值 对应的 小于这个数值的个数 for (int i = 0; i < nums.size(); i++) { vec[i] = hash[nums[i]]; @@ -92,7 +92,7 @@ for (int i = 0; i < nums.size(); i++) { 关键地方讲完了,整体C++代码如下: -```C++ +```CPP class Solution { public: vector smallerNumbersThanCurrent(vector& nums) { @@ -120,8 +120,51 @@ public: ## Java ```java +/** +* 解法一:暴力 +* 时间复杂度:O(n^2) +* 空间复杂度:O(n) +*/ +class Solution { + public int[] smallerNumbersThanCurrent(int[] nums) { + int[] res = new int[nums.length]; + for (int i = 0; i < nums.length; i++) { + for (int j = 0; j < nums.length; j++) { + if (nums[j] < nums[i] && j != i) { // 注意 j 不能和 i 重合 + res[i]++; + } + } + } + return res; + } +} ``` +```java +/** +* 优化:排序 + 哈希表 +* 时间复杂度:O(nlogn) +* 空间复杂度:O(n) +*/ +class Solution { + public int[] smallerNumbersThanCurrent(int[] nums) { + int[] res = Arrays.copyOf(nums, nums.length); + Arrays.sort(res); // 是对 res 排序,nums 中顺序还要保持 + int[] hash = new int[101]; // 使用哈希表,记录比当前元素小的元素个数 + for (int i = res.length - 1; i >= 0; i--) { // 注意:从后向前 + hash[res[i]] = i; // 排序后,当前下标即表示比当前元素小的元素个数 + } + // 此时 hash中保存的每一个元素数值 便是 小于这个数值的个数 + for (int i = 0; i < res.length; i++) { + res[i] = hash[nums[i]]; + } + return res; + } +} +``` + + + ## Python ```python @@ -141,6 +184,5 @@ public: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
- +
diff --git a/problems/1365.有多少小于当前数字的数字.md b/problems/1365.有多少小于当前数字的数字.md index 6324329c..156efea6 100644 --- a/problems/1365.有多少小于当前数字的数字.md +++ b/problems/1365.有多少小于当前数字的数字.md @@ -69,7 +69,7 @@ sort(vec.begin(), vec.end()); // 从小到大排序之后,元素下标就是 这里就需要一个技巧了,**在构造数组hash的时候,从后向前遍历,这样hash里存放的就是相同元素最左面的数值和下标了**。 代码如下: -```C++ +```CPP int hash[101]; for (int i = vec.size() - 1; i >= 0; i--) { // 从后向前,记录 vec[i] 对应的下标 hash[vec[i]] = i; @@ -80,7 +80,7 @@ for (int i = vec.size() - 1; i >= 0; i--) { // 从后向前,记录 vec[i] 对 代码如下: -```C++ +```CPP // 此时hash里保存的每一个元素数值 对应的 小于这个数值的个数 for (int i = 0; i < nums.size(); i++) { vec[i] = hash[nums[i]]; @@ -93,7 +93,7 @@ for (int i = 0; i < nums.size(); i++) { 关键地方讲完了,整体C++代码如下: -```C++ +```CPP class Solution { public: vector smallerNumbersThanCurrent(vector& nums) { @@ -139,7 +139,19 @@ public int[] smallerNumbersThanCurrent(int[] nums) { ``` Python: - +```python +class Solution: + def smallerNumbersThanCurrent(self, nums: List[int]) -> List[int]: + res = nums[:] + hash = dict() + res.sort() # 从小到大排序之后,元素下标就是小于当前数字的数字 + for i, num in enumerate(res): + if num not in hash.keys(): # 遇到了相同的数字,那么不需要更新该 number 的情况 + hash[num] = i + for i, num in enumerate(nums): + res[i] = hash[num] + return res +``` Go: JavaScript: @@ -150,4 +162,4 @@ JavaScript: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/1382.将二叉搜索树变平衡.md b/problems/1382.将二叉搜索树变平衡.md index 268f21c5..758d5ad8 100644 --- a/problems/1382.将二叉搜索树变平衡.md +++ b/problems/1382.将二叉搜索树变平衡.md @@ -42,7 +42,7 @@ 代码如下: -```C++ +```CPP class Solution { private: vector vec; @@ -55,7 +55,7 @@ private: vec.push_back(cur->val); traversal(cur->right); } - 有序数组转平衡二叉树 + // 有序数组转平衡二叉树 TreeNode* getTree(vector& nums, int left, int right) { if (left > right) return nullptr; int mid = left + ((right - left) / 2); @@ -76,9 +76,53 @@ public: # 其他语言版本 Java: - +```java +class Solution { + ArrayList res = new ArrayList(); + // 有序树转成有序数组 + private void travesal(TreeNode cur) { + if (cur == null) return; + travesal(cur.left); + res.add(cur.val); + travesal(cur.right); + } + // 有序数组转成平衡二叉树 + private TreeNode getTree(ArrayList nums, int left, int right) { + if (left > right) return null; + int mid = left + (right - left) / 2; + TreeNode root = new TreeNode(nums.get(mid)); + root.left = getTree(nums, left, mid - 1); + root.right = getTree(nums, mid + 1, right); + return root; + } + public TreeNode balanceBST(TreeNode root) { + travesal(root); + return getTree(res, 0, res.size() - 1); + } +} +``` Python: - +```python +class Solution: + def balanceBST(self, root: TreeNode) -> TreeNode: + res = [] + # 有序树转成有序数组 + def traversal(cur: TreeNode): + if not cur: return + traversal(cur.left) + res.append(cur.val) + traversal(cur.right) + # 有序数组转成平衡二叉树 + def getTree(nums: List, left, right): + if left > right: return + mid = left + (right -left) // 2 + root = TreeNode(nums[mid]) + root.left = getTree(nums, left, mid - 1) + root.right = getTree(nums, mid + 1, right) + return root + traversal(root) + return getTree(res, 0, len(res) - 1) +``` Go: JavaScript: @@ -87,6 +131,6 @@ JavaScript: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/O(n)的算法居然超时了,此时的n究竟是多大?.md b/problems/O(n)的算法居然超时了,此时的n究竟是多大?.md index 2b995c59..6897c01f 100644 --- a/problems/O(n)的算法居然超时了,此时的n究竟是多大?.md +++ b/problems/O(n)的算法居然超时了,此时的n究竟是多大?.md @@ -67,7 +67,7 @@ 实现三个函数,时间复杂度分别是 O(n) , O(n^2), O(nlogn),使用加法运算来统一测试。 -```C++ +```CPP // O(n) void function1(long long n) { long long k = 0; @@ -78,7 +78,7 @@ void function1(long long n) { ``` -```C++ +```CPP // O(n^2) void function2(long long n) { long long k = 0; @@ -91,7 +91,7 @@ void function2(long long n) { } ``` -```C++ +```CPP // O(nlogn) void function3(long long n) { long long k = 0; @@ -105,7 +105,7 @@ void function3(long long n) { ``` 来看一下这三个函数随着n的规模变化,耗时会产生多大的变化,先测function1 ,就把 function2 和 function3 注释掉 -```C++ +```CPP int main() { long long n; // 数据规模 while (1) { @@ -154,7 +154,7 @@ O(nlogn)的算法,1s内大概计算机可以运行 2 * (10^7)次计算,符 # 完整测试代码 -```C++ +```CPP #include #include #include @@ -235,4 +235,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/为了绝杀编辑距离,卡尔做了三步铺垫.md b/problems/为了绝杀编辑距离,卡尔做了三步铺垫.md index 353bd68e..4a0ef0d2 100644 --- a/problems/为了绝杀编辑距离,卡尔做了三步铺垫.md +++ b/problems/为了绝杀编辑距离,卡尔做了三步铺垫.md @@ -58,7 +58,7 @@ else dp[i][j] = dp[i][j - 1]; 状态转移方程: -```C++ +```CPP if (s[i - 1] == t[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]; } else { @@ -89,7 +89,7 @@ if (s[i - 1] == t[j - 1]) { 那最后当然是取最小值,所以当word1[i - 1] 与 word2[j - 1]不相同的时候,递推公式:dp[i][j] = min({dp[i - 1][j - 1] + 2, dp[i - 1][j] + 1, dp[i][j - 1] + 1}); 状态转移方程: -```C++ +```CPP if (word1[i - 1] == word2[j - 1]) { dp[i][j] = dp[i - 1][j - 1]; } else { @@ -150,7 +150,7 @@ if (word1[i - 1] != word2[j - 1]),此时就需要编辑了,如何编辑呢 递归公式代码如下: -```C++ +```CPP if (word1[i - 1] == word2[j - 1]) { dp[i][j] = dp[i - 1][j - 1]; } @@ -181,4 +181,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/二叉树中递归带着回溯.md b/problems/二叉树中递归带着回溯.md index 372dc40c..0f768e0f 100644 --- a/problems/二叉树中递归带着回溯.md +++ b/problems/二叉树中递归带着回溯.md @@ -19,7 +19,7 @@ 那么如下我再给出求100. 相同的树 的代码,如下: -```C++ +```CPP class Solution { public: bool compare(TreeNode* tree1, TreeNode* tree2) { @@ -52,7 +52,7 @@ public: 如下的代码充分的体现出回溯:(257. 二叉树的所有路径) -```C++ +```CPP class Solution { private: @@ -120,13 +120,13 @@ public: 为了把这份精简代码的回溯过程展现出来,大家可以试一试把: -``` +```CPP if (cur->left) traversal(cur->left, path + "->", result); // 左 回溯就隐藏在这里 ``` 改成如下代码: -``` +```CPP path += "->"; traversal(cur->left, path, result); // 左 ``` @@ -149,7 +149,7 @@ if (cur->right) { 如果想把回溯加上,就要 在上面代码的基础上,加上回溯,就可以AC了。 -``` +```CPP if (cur->left) { path += "->"; traversal(cur->left, path, result); // 左 @@ -448,4 +448,4 @@ func traversal(root *TreeNode,result *[]string,path *[]int){ * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/二叉树总结篇.md b/problems/二叉树总结篇.md index d4af8aae..43276ec9 100644 --- a/problems/二叉树总结篇.md +++ b/problems/二叉树总结篇.md @@ -174,4 +174,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/二叉树理论基础.md b/problems/二叉树理论基础.md index a720672e..edd1fed4 100644 --- a/problems/二叉树理论基础.md +++ b/problems/二叉树理论基础.md @@ -223,10 +223,18 @@ type TreeNode struct { } ``` +JavaScript: +``` +function TreeNode(val, left, right) { + this.val = (val===undefined ? 0 : val) + this.left = (left===undefined ? null : left) + this.right = (right===undefined ? null : right) +} +``` ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/二叉树的统一迭代法.md b/problems/二叉树的统一迭代法.md index 9e24fef6..d105f4b8 100644 --- a/problems/二叉树的统一迭代法.md +++ b/problems/二叉树的统一迭代法.md @@ -34,7 +34,7 @@ 中序遍历代码如下:(详细注释) -```C++ +```CPP class Solution { public: vector inorderTraversal(TreeNode* root) { @@ -77,7 +77,7 @@ public: 迭代法前序遍历代码如下: (**注意此时我们和中序遍历相比仅仅改变了两行代码的顺序**) -```C++ +```CPP class Solution { public: vector preorderTraversal(TreeNode* root) { @@ -108,7 +108,7 @@ public: 后续遍历代码如下: (**注意此时我们和中序遍历相比仅仅改变了两行代码的顺序**) -```C++ +```CPP class Solution { public: vector postorderTraversal(TreeNode* root) { @@ -530,4 +530,4 @@ var postorderTraversal = function(root, res = []) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/二叉树的迭代遍历.md b/problems/二叉树的迭代遍历.md index 20397773..faa580e0 100644 --- a/problems/二叉树的迭代遍历.md +++ b/problems/二叉树的迭代遍历.md @@ -37,7 +37,7 @@ 不难写出如下代码: (**注意代码中空节点不入栈**) -```C++ +```CPP class Solution { public: vector preorderTraversal(TreeNode* root) { @@ -84,7 +84,7 @@ public: **中序遍历,可以写出如下代码:** -```C++ +```CPP class Solution { public: vector inorderTraversal(TreeNode* root) { @@ -116,7 +116,7 @@ public: **所以后序遍历只需要前序遍历的代码稍作修改就可以了,代码如下:** -```C++ +```CPP class Solution { public: vector postorderTraversal(TreeNode* root) { @@ -473,4 +473,4 @@ var postorderTraversal = function(root, res = []) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/二叉树的递归遍历.md b/problems/二叉树的递归遍历.md index 2b5a44dd..223cf722 100644 --- a/problems/二叉树的递归遍历.md +++ b/problems/二叉树的递归遍历.md @@ -58,7 +58,7 @@ traversal(cur->right, vec); // 右 前序遍历: -```C++ +```CPP class Solution { public: void traversal(TreeNode* cur, vector& vec) { @@ -79,7 +79,7 @@ public: 中序遍历: -```C++ +```CPP void traversal(TreeNode* cur, vector& vec) { if (cur == NULL) return; traversal(cur->left, vec); // 左 @@ -90,7 +90,7 @@ void traversal(TreeNode* cur, vector& vec) { 后序遍历: -```C++ +```CPP void traversal(TreeNode* cur, vector& vec) { if (cur == NULL) return; traversal(cur->left, vec); // 左 @@ -222,7 +222,7 @@ Go: 前序遍历: ```go -func PreorderTraversal(root *TreeNode) (res []int) { +func preorderTraversal(root *TreeNode) (res []int) { var traversal func(node *TreeNode) traversal = func(node *TreeNode) { if node == nil { @@ -240,7 +240,7 @@ func PreorderTraversal(root *TreeNode) (res []int) { 中序遍历: ```go -func InorderTraversal(root *TreeNode) (res []int) { +func inorderTraversal(root *TreeNode) (res []int) { var traversal func(node *TreeNode) traversal = func(node *TreeNode) { if node == nil { @@ -257,7 +257,7 @@ func InorderTraversal(root *TreeNode) (res []int) { 后序遍历: ```go -func PostorderTraversal(root *TreeNode) (res []int) { +func postorderTraversal(root *TreeNode) (res []int) { var traversal func(node *TreeNode) traversal = func(node *TreeNode) { if node == nil { @@ -367,4 +367,4 @@ var postorderTraversal = function(root) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/关于时间复杂度,你不知道的都在这里!.md b/problems/关于时间复杂度,你不知道的都在这里!.md index fe378228..7ff9b470 100644 --- a/problems/关于时间复杂度,你不知道的都在这里!.md +++ b/problems/关于时间复杂度,你不知道的都在这里!.md @@ -177,4 +177,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/BAT级别技术面试流程和注意事项都在这里了.md b/problems/前序/BAT级别技术面试流程和注意事项都在这里了.md index fbcdf970..c5797739 100644 --- a/problems/前序/BAT级别技术面试流程和注意事项都在这里了.md +++ b/problems/前序/BAT级别技术面试流程和注意事项都在这里了.md @@ -221,4 +221,4 @@ leetcode是专门针对算法练习的题库,leetcode现在也推出了中文 * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/On的算法居然超时了,此时的n究竟是多大?.md b/problems/前序/On的算法居然超时了,此时的n究竟是多大?.md index f6218777..d9cc8d45 100644 --- a/problems/前序/On的算法居然超时了,此时的n究竟是多大?.md +++ b/problems/前序/On的算法居然超时了,此时的n究竟是多大?.md @@ -67,7 +67,7 @@ 实现三个函数,时间复杂度分别是 O(n) , O(n^2), O(nlogn),使用加法运算来统一测试。 -```C++ +```CPP // O(n) void function1(long long n) { long long k = 0; @@ -78,7 +78,7 @@ void function1(long long n) { ``` -```C++ +```CPP // O(n^2) void function2(long long n) { long long k = 0; @@ -91,7 +91,7 @@ void function2(long long n) { } ``` -```C++ +```CPP // O(nlogn) void function3(long long n) { long long k = 0; @@ -105,7 +105,7 @@ void function3(long long n) { ``` 来看一下这三个函数随着n的规模变化,耗时会产生多大的变化,先测function1 ,就把 function2 和 function3 注释掉 -```C++ +```CPP int main() { long long n; // 数据规模 while (1) { @@ -154,7 +154,7 @@ O(nlogn)的算法,1s内大概计算机可以运行 2 * (10^7)次计算,符 # 完整测试代码 -```C++ +```CPP #include #include #include @@ -228,4 +228,4 @@ int main() { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/上海互联网公司总结.md b/problems/前序/上海互联网公司总结.md index 386a0a93..05cbe1b0 100644 --- a/problems/前序/上海互联网公司总结.md +++ b/problems/前序/上海互联网公司总结.md @@ -133,4 +133,4 @@ * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/什么是核心代码模式,什么又是ACM模式?.md b/problems/前序/什么是核心代码模式,什么又是ACM模式?.md index 5a19a72a..3c5fb4e4 100644 --- a/problems/前序/什么是核心代码模式,什么又是ACM模式?.md +++ b/problems/前序/什么是核心代码模式,什么又是ACM模式?.md @@ -13,7 +13,7 @@ 而力扣上是核心代码模式,就是把要处理的数据都已经放入容器里,可以直接写逻辑,例如这样: -```C++ +```CPP class Solution { public: int minimumTotal(vector>& triangle) { @@ -55,7 +55,7 @@ public: 这道题如果要是力扣上的核心代码模式,OJ应该直接给出如下代码: -```C++ +```CPP class Solution { public: int getDays(vector& work, vector& gym) { @@ -72,7 +72,7 @@ ACM模式要求写出来的代码是直接可以本地运行的,所以我们 拿本题来说,为了让代码可以运行,需要include这些库函数: -```C++ +```CPP #include #include using namespace std; @@ -87,7 +87,7 @@ using namespace std; 完整代码如下: -```C++ +```CPP #include #include using namespace std; @@ -122,4 +122,4 @@ int main() { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/代码风格.md b/problems/前序/代码风格.md index a9eabd0c..a71ac420 100644 --- a/problems/前序/代码风格.md +++ b/problems/前序/代码风格.md @@ -108,7 +108,7 @@ while (n) { ``` 以下是我刚写的力扣283.移动零的代码,大家可以看一下整体风格,注意空格的细节! -```C++ +```CPP class Solution { public: void moveZeroes(vector& nums) { @@ -149,4 +149,4 @@ Google规范是 大括号和 控制语句保持同一行的,我个人也很认 * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/关于时间复杂度,你不知道的都在这里!.md b/problems/前序/关于时间复杂度,你不知道的都在这里!.md index d6471b99..d4869b14 100644 --- a/problems/前序/关于时间复杂度,你不知道的都在这里!.md +++ b/problems/前序/关于时间复杂度,你不知道的都在这里!.md @@ -173,4 +173,4 @@ O(2 * n^2 + 10 * n + 1000) < O(3 * n^2),所以说最后省略掉常数项系 * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/关于空间复杂度,可能有几个疑问?.md b/problems/前序/关于空间复杂度,可能有几个疑问?.md index 0208aa91..5b2f73e2 100644 --- a/problems/前序/关于空间复杂度,可能有几个疑问?.md +++ b/problems/前序/关于空间复杂度,可能有几个疑问?.md @@ -30,7 +30,7 @@ 2. 空间复杂度是准确算出程序运行时所占用的内存么? -不要以为空间复杂度就已经精准的掌握了程序的内存使用大小,很有多因素会影响程序真正内存使用大小,例如编译器的内存对齐,编程语言容器的底层实现等等这些都会影响到程序内存的开销。 +不要以为空间复杂度就已经精准的掌握了程序的内存使用大小,很多因素会影响程序真正内存使用大小,例如编译器的内存对齐,编程语言容器的底层实现等等这些都会影响到程序内存的开销。 所以空间复杂度是预先大体评估程序内存使用的大小。 @@ -42,7 +42,7 @@ 来看一下例子,什么时候的空间复杂度是O(1)呢,C++代码如下: -```C++ +```CPP int j = 0; for (int i = 0; i < n; i++) { j++; @@ -54,7 +54,7 @@ for (int i = 0; i < n; i++) { 什么时候的空间复杂度是O(n)? 当消耗空间和输入参数n保持线性增长,这样的空间复杂度为O(n),来看一下这段C++代码 -```C++ +```CPP int* a = new int(n); for (int i = 0; i < n; i++) { a[i] = i; @@ -76,4 +76,4 @@ for (int i = 0; i < n; i++) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/刷了这么多题,你了解自己代码的内存消耗么?.md b/problems/前序/刷了这么多题,你了解自己代码的内存消耗么?.md index 6d3b4931..3fccfb22 100644 --- a/problems/前序/刷了这么多题,你了解自己代码的内存消耗么?.md +++ b/problems/前序/刷了这么多题,你了解自己代码的内存消耗么?.md @@ -77,7 +77,7 @@ 可以看一下这段C++代码输出的各个数据类型大小是多少? -```C++ +```CPP struct node{ int num; char cha; @@ -153,4 +153,4 @@ char型的数据和int型的数据挨在一起,该int数据从地址1开始, * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/力扣上的代码想在本地编译运行?.md b/problems/前序/力扣上的代码想在本地编译运行?.md index f4a9b0f3..a0fcbcd8 100644 --- a/problems/前序/力扣上的代码想在本地编译运行?.md +++ b/problems/前序/力扣上的代码想在本地编译运行?.md @@ -27,7 +27,7 @@ 力扣746. 使用最小花费爬楼梯,完整的可以在直接本地运行的C++代码如下: -```C++ +```CPP #include #include using namespace std; @@ -70,4 +70,4 @@ int main() { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/北京互联网公司总结.md b/problems/前序/北京互联网公司总结.md index 4dcaa691..c770759f 100644 --- a/problems/前序/北京互联网公司总结.md +++ b/problems/前序/北京互联网公司总结.md @@ -119,4 +119,4 @@ * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/广州互联网公司总结.md b/problems/前序/广州互联网公司总结.md index e9b2af00..3cc18c8e 100644 --- a/problems/前序/广州互联网公司总结.md +++ b/problems/前序/广州互联网公司总结.md @@ -82,4 +82,4 @@ * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/成都互联网公司总结.md b/problems/前序/成都互联网公司总结.md index 2435ccb2..d44800cd 100644 --- a/problems/前序/成都互联网公司总结.md +++ b/problems/前序/成都互联网公司总结.md @@ -80,4 +80,4 @@ * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/杭州互联网公司总结.md b/problems/前序/杭州互联网公司总结.md index e2691469..aebf5779 100644 --- a/problems/前序/杭州互联网公司总结.md +++ b/problems/前序/杭州互联网公司总结.md @@ -91,4 +91,4 @@ * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/深圳互联网公司总结.md b/problems/前序/深圳互联网公司总结.md index 4b68dad6..9e089315 100644 --- a/problems/前序/深圳互联网公司总结.md +++ b/problems/前序/深圳互联网公司总结.md @@ -85,4 +85,4 @@ * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/程序员写文档工具.md b/problems/前序/程序员写文档工具.md index b06ce0ad..b76fb036 100644 --- a/problems/前序/程序员写文档工具.md +++ b/problems/前序/程序员写文档工具.md @@ -140,4 +140,4 @@ Markdown支持部分html,例如这样 * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/程序员简历.md b/problems/前序/程序员简历.md index a9abcdc4..f47516dc 100644 --- a/problems/前序/程序员简历.md +++ b/problems/前序/程序员简历.md @@ -136,4 +136,4 @@ Carl校招社招都拿过大厂的offer,同时也看过很多应聘者的简 * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/递归算法的时间与空间复杂度分析.md b/problems/前序/递归算法的时间与空间复杂度分析.md index f1501e8a..b6c79f25 100644 --- a/problems/前序/递归算法的时间与空间复杂度分析.md +++ b/problems/前序/递归算法的时间与空间复杂度分析.md @@ -18,7 +18,7 @@ 先来看一下求斐波那契数的递归写法。 -```C++ +```CPP int fibonacci(int i) { if(i <= 0) return 0; if(i == 1) return 1; @@ -50,7 +50,7 @@ int fibonacci(int i) { 以下为C++代码,来测一下,让我们输入n的时候,这段递归求斐波那契代码的耗时。 -```C++ +```CPP #include #include #include @@ -99,7 +99,7 @@ int main() 其实罪魁祸首就是这里的两次递归,导致了时间复杂度以指数上升。 -```C++ +```CPP return fibonacci(i-1) + fibonacci(i-2); ``` @@ -107,7 +107,7 @@ return fibonacci(i-1) + fibonacci(i-2); 来看一下如下代码: -```C++ +```CPP // 版本二 int fibonacci(int first, int second, int n) { if (n <= 0) { @@ -138,7 +138,7 @@ int fibonacci(int first, int second, int n) { 此时再来测一下耗时情况验证一下: -```C++ +```CPP #include #include #include @@ -208,7 +208,7 @@ int main() 那么每次递归的空间复杂度是O(1), 调用栈深度为n,所以这段递归代码的空间复杂度就是O(n)。 -```C++ +```CPP int fibonacci(int i) { if(i <= 0) return 0; if(i == 1) return 1; @@ -227,7 +227,7 @@ int fibonacci(int i) { 带大家再分析一段二分查找的递归实现。 -```C++ +```CPP int binary_search( int arr[], int l, int r, int x) { if (r >= l) { int mid = l + (r - l) / 2; @@ -272,4 +272,4 @@ int binary_search( int arr[], int l, int r, int x) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/前序/通过一道面试题目,讲一讲递归算法的时间复杂度!.md b/problems/前序/通过一道面试题目,讲一讲递归算法的时间复杂度!.md index 16ba8361..b3aef43c 100644 --- a/problems/前序/通过一道面试题目,讲一讲递归算法的时间复杂度!.md +++ b/problems/前序/通过一道面试题目,讲一讲递归算法的时间复杂度!.md @@ -27,7 +27,7 @@ 想一下这么简单的一道题目,代码应该如何写呢。最直观的方式应该就是,一个for循环求出结果,代码如下: -```C++ +```CPP int function1(int x, int n) { int result = 1; // 注意 任何数的0次方等于1 for (int i = 0; i < n; i++) { @@ -44,7 +44,7 @@ int function1(int x, int n) { 那么就可以写出了如下这样的一个递归的算法,使用递归解决了这个问题。 -``` +```CPP int function2(int x, int n) { if (n == 0) { return 1; // return 1 同样是因为0次方是等于1的 @@ -62,7 +62,7 @@ int function2(int x, int n) { 这个时间复杂度就没有达到面试官的预期。于是又写出了如下的递归算法的代码: -``` +```CPP int function3(int x, int n) { if (n == 0) { return 1; @@ -101,7 +101,7 @@ int function3(int x, int n) { 于是又写出如下递归算法的代码: -``` +```CPP int function4(int x, int n) { if (n == 0) { return 1; @@ -132,7 +132,7 @@ int function4(int x, int n) { 对于function3 这样的递归实现,很容易让人感觉这是O(logn)的时间复杂度,其实这是O(n)的算法! -``` +```CPP int function3(int x, int n) { if (n == 0) { return 1; @@ -145,16 +145,14 @@ int function3(int x, int n) { ``` 可以看出这道题目非常简单,但是又很考究算法的功底,特别是对递归的理解,这也是我面试别人的时候用过的一道题,所以整个情景我才写的如此逼真,哈哈。 -大厂面试的时候最喜欢用“简单题”来考察候选人的算法功底,注意这里的“简单题”可并不一定真的简单​哦! +大厂面试的时候最喜欢用“简单题”来考察候选人的算法功底,注意这里的“简单题”可并不一定真的简单哦! 如果认真读完本篇,相信大家对递归算法的有一个新的认识的,同一道题目,同样是递归,效率可是不一样的! - - ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/剑指Offer05.替换空格.md b/problems/剑指Offer05.替换空格.md index 3e4e3631..4ae5f9f2 100644 --- a/problems/剑指Offer05.替换空格.md +++ b/problems/剑指Offer05.替换空格.md @@ -46,7 +46,7 @@ i指向新长度的末尾,j指向旧长度的末尾。 C++代码如下: -```C++ +```CPP class Solution { public: string replaceSpace(string s) { @@ -288,4 +288,4 @@ javaScript: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/剑指Offer58-II.左旋转字符串.md b/problems/剑指Offer58-II.左旋转字符串.md index 3c3eaef0..1073dafa 100644 --- a/problems/剑指Offer58-II.左旋转字符串.md +++ b/problems/剑指Offer58-II.左旋转字符串.md @@ -58,7 +58,7 @@ https://leetcode-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/ C++代码如下: -```C++ +```CPP class Solution { public: string reverseLeftWords(string s, int n) { @@ -141,6 +141,18 @@ class Solution: # 空间复杂度:O(n),python的string为不可变,需要开辟同样大小的list空间来修改 ``` +```python 3 +#方法三:考虑不能用切片的情况下,利用模+下标实现 +class Solution: + def reverseLeftWords(self, s: str, n: int) -> str: + new_s = '' + for i in range(len(s)): + j = (i+n)%len(s) + new_s = new_s + s[j] + return new_s + +``` + Go: ```go @@ -165,6 +177,22 @@ func reverse(b []byte, left, right int){ ``` +JavaScript: + +```javascript +var reverseLeftWords = function (s, n) { + const reverse = (str, left, right) => { + let strArr = str.split(""); + for (; left < right; left++, right--) { + [strArr[left], strArr[right]] = [strArr[right], strArr[left]]; + } + return strArr.join(""); + } + s = reverse(s, 0, n - 1); + s = reverse(s, n, s.length - 1); + return reverse(s, 0, s.length - 1); +}; +``` @@ -173,4 +201,4 @@ func reverse(b []byte, left, right int){ * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/动态规划-股票问题总结篇.md b/problems/动态规划-股票问题总结篇.md index 590a8008..d186aaf9 100644 --- a/problems/动态规划-股票问题总结篇.md +++ b/problems/动态规划-股票问题总结篇.md @@ -24,7 +24,7 @@ 【贪心解法】 取最左最小值,取最右最大值,那么得到的差值就是最大利润,代码如下: -```C++ +```CPP class Solution { public: int maxProfit(vector& prices) { @@ -56,7 +56,7 @@ public: 代码如下: -```C++ +```CPP // 版本一 class Solution { public: @@ -79,7 +79,7 @@ public: 使用滚动数组,代码如下: -```C++ +```CPP // 版本二 class Solution { public: @@ -110,7 +110,7 @@ public: 收集每天的正利润便可,代码如下: -```C++ +```CPP class Solution { public: int maxProfit(vector& prices) { @@ -146,7 +146,7 @@ dp数组定义: 代码如下:(注意代码中的注释,标记了和121.买卖股票的最佳时机唯一不同的地方) -```C++ +```CPP class Solution { public: int maxProfit(vector& prices) { @@ -204,7 +204,7 @@ dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]); 代码如下: -```C++ +```CPP // 版本一 class Solution { public: @@ -230,7 +230,7 @@ public: 当然,大家可以看到力扣官方题解里的一种优化空间写法,我这里给出对应的C++版本: -```C++ +```CPP // 版本二 class Solution { public: @@ -291,7 +291,7 @@ dp[i][2] = max(dp[i - 1][i] + prices[i], dp[i][2]) 同理可以类比剩下的状态,代码如下: -```C++ +```CPP for (int j = 0; j < 2 * k - 1; j += 2) { dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]); dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]); @@ -300,7 +300,7 @@ for (int j = 0; j < 2 * k - 1; j += 2) { 整体代码如下: -```C++ +```CPP class Solution { public: int maxProfit(int k, vector& prices) { @@ -375,7 +375,7 @@ p[i][3] = dp[i - 1][2]; 综上分析,递推代码如下: -```C++ +```CPP dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3]- prices[i], dp[i - 1][1]) - prices[i]; dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]); dp[i][2] = dp[i - 1][0] + prices[i]; @@ -384,7 +384,7 @@ dp[i][3] = dp[i - 1][2]; 整体代码如下: -```C++ +```CPP class Solution { public: int maxProfit(vector& prices) { @@ -439,7 +439,7 @@ dp[i][1] 表示第i天不持有股票所得最多现金 以上分析完毕,代码如下: -```C++ +```CPP class Solution { public: int maxProfit(vector& prices, int fee) { @@ -486,4 +486,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/动态规划总结篇.md b/problems/动态规划总结篇.md index 797f426a..d1c52005 100644 --- a/problems/动态规划总结篇.md +++ b/problems/动态规划总结篇.md @@ -136,5 +136,5 @@ * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/动态规划理论基础.md b/problems/动态规划理论基础.md index 250fa57d..c9491da1 100644 --- a/problems/动态规划理论基础.md +++ b/problems/动态规划理论基础.md @@ -140,4 +140,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/双指针总结.md b/problems/双指针总结.md index b03d3ff2..df7cffae 100644 --- a/problems/双指针总结.md +++ b/problems/双指针总结.md @@ -100,4 +100,4 @@ for (int i = 0; i < array.size(); i++) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/周总结/20200927二叉树周末总结.md b/problems/周总结/20200927二叉树周末总结.md index a6d73c01..f81cc6bb 100644 --- a/problems/周总结/20200927二叉树周末总结.md +++ b/problems/周总结/20200927二叉树周末总结.md @@ -13,7 +13,7 @@ 对于二叉树节点的定义,C++代码如下: -```C++ +```CPP struct TreeNode { int val; TreeNode *left; @@ -35,7 +35,7 @@ TreeNode* a = new TreeNode(9); 没有构造函数的话就要这么写: -```C++ +```CPP TreeNode* a = new TreeNode(); a->val = 9; a->left = NULL; @@ -66,7 +66,7 @@ morris遍历是二叉树遍历算法的超强进阶算法,morris遍历可以 拿前序遍历来举例,空节点入栈: -```C++ +```CPP class Solution { public: vector preorderTraversal(TreeNode* root) { @@ -88,7 +88,7 @@ public: 前序遍历空节点不入栈的代码:(注意注释部分和上文的区别) -```C++ +```CPP class Solution { public: vector preorderTraversal(TreeNode* root) { @@ -203,3 +203,4 @@ public: **本周我们都是讲解了二叉树,从理论基础到遍历方式,从递归到迭代,从深度遍历到广度遍历,最后再用了一个翻转二叉树的题目把我们之前讲过的遍历方式都串了起来。** +
diff --git a/problems/周总结/20201003二叉树周末总结.md b/problems/周总结/20201003二叉树周末总结.md index 0cb8b654..db89fc82 100644 --- a/problems/周总结/20201003二叉树周末总结.md +++ b/problems/周总结/20201003二叉树周末总结.md @@ -21,7 +21,7 @@ 100.相同的树的递归代码如下: -```C++ +```CPP class Solution { public: bool compare(TreeNode* left, TreeNode* right) { @@ -48,7 +48,7 @@ public: 100.相同的树,精简之后代码如下: -```C++ +```CPP class Solution { public: bool compare(TreeNode* left, TreeNode* right) { @@ -67,7 +67,7 @@ public: 100.相同的树,迭代法代码如下: -```C++ +```CPP class Solution { public: @@ -109,7 +109,7 @@ public: **而根节点的高度就是二叉树的最大深度**,所以本题中我们通过后序求的根节点高度来求的二叉树最大深度,所以[二叉树:看看这些树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中使用的是后序遍历。 本题当然也可以使用前序,代码如下:(**充分表现出求深度回溯的过程**) -```C++ +```CPP class Solution { public: int result; @@ -143,7 +143,7 @@ public: 注意以上代码是为了把细节体现出来,简化一下代码如下: -```C++ +```CPP class Solution { public: int result; @@ -223,12 +223,12 @@ public: 文中我明确的说了:**回溯就隐藏在traversal(cur->left, path + "->", result);中的 path + "->"。 每次函数调用完,path依然是没有加上"->" 的,这就是回溯了。** 如果还不理解的话,可以把 -``` +```CPP traversal(cur->left, path + "->", result); ``` 改成 -``` +```CPP string tmp = path + "->"; traversal(cur->left, tmp, result); ``` @@ -255,3 +255,4 @@ traversal(cur->left, tmp, result); * 知乎:[代码随想录](https://www.zhihu.com/people/sun-xiu-yang-64) ![](https://img-blog.csdnimg.cn/2021013018121150.png) +
diff --git a/problems/周总结/20201010二叉树周末总结.md b/problems/周总结/20201010二叉树周末总结.md index d62fa5a5..7b1e7f68 100644 --- a/problems/周总结/20201010二叉树周末总结.md +++ b/problems/周总结/20201010二叉树周末总结.md @@ -87,3 +87,4 @@ **如果大家一路跟下来,一定收获满满,如果周末不做这个总结,大家可能都不知道自己收获满满,啊哈!** +
diff --git a/problems/周总结/20201017二叉树周末总结.md b/problems/周总结/20201017二叉树周末总结.md index e642bfb2..c061a48e 100644 --- a/problems/周总结/20201017二叉树周末总结.md +++ b/problems/周总结/20201017二叉树周末总结.md @@ -116,3 +116,4 @@ 大家如果每天坚持跟下来,会发现又是充实的一周![机智] +
diff --git a/problems/周总结/20201030回溯周末总结.md b/problems/周总结/20201030回溯周末总结.md index cbb0eb8a..54cdc3b5 100644 --- a/problems/周总结/20201030回溯周末总结.md +++ b/problems/周总结/20201030回溯周末总结.md @@ -112,4 +112,4 @@ * B站:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -![](../pics/公众号.png) +
diff --git a/problems/周总结/20201107回溯周末总结.md b/problems/周总结/20201107回溯周末总结.md index 8f2e762d..66036f64 100644 --- a/problems/周总结/20201107回溯周末总结.md +++ b/problems/周总结/20201107回溯周末总结.md @@ -167,3 +167,4 @@ leetcode上的计时应该是以4ms为单位,有的多提交几次,多个4ms +
diff --git a/problems/周总结/20201112回溯周末总结.md b/problems/周总结/20201112回溯周末总结.md index 886b8923..9ca3cff1 100644 --- a/problems/周总结/20201112回溯周末总结.md +++ b/problems/周总结/20201112回溯周末总结.md @@ -95,3 +95,4 @@ +
diff --git a/problems/周总结/20201126贪心周末总结.md b/problems/周总结/20201126贪心周末总结.md index 215e8f01..ea504991 100644 --- a/problems/周总结/20201126贪心周末总结.md +++ b/problems/周总结/20201126贪心周末总结.md @@ -112,3 +112,4 @@ public: +
diff --git a/problems/周总结/20201203贪心周末总结.md b/problems/周总结/20201203贪心周末总结.md index 43e877dd..a82c5d4a 100644 --- a/problems/周总结/20201203贪心周末总结.md +++ b/problems/周总结/20201203贪心周末总结.md @@ -96,3 +96,4 @@ +
diff --git a/problems/周总结/20201210复杂度分析周末总结.md b/problems/周总结/20201210复杂度分析周末总结.md index 1833c1ad..8fe13f0b 100644 --- a/problems/周总结/20201210复杂度分析周末总结.md +++ b/problems/周总结/20201210复杂度分析周末总结.md @@ -119,3 +119,4 @@ 就酱,「代码随想录」是技术公众号里的一抹清流,值得推荐给身边的朋友同学们! +
diff --git a/problems/周总结/20201217贪心周末总结.md b/problems/周总结/20201217贪心周末总结.md index 4a634da5..4363027c 100644 --- a/problems/周总结/20201217贪心周末总结.md +++ b/problems/周总结/20201217贪心周末总结.md @@ -97,3 +97,4 @@ 而且大家也会发现,贪心并没有想象中的那么简单,贪心往往妙的出其不意,触不及防!哈哈 +
diff --git a/problems/周总结/20201224贪心周末总结.md b/problems/周总结/20201224贪心周末总结.md index cdc62168..a82433cf 100644 --- a/problems/周总结/20201224贪心周末总结.md +++ b/problems/周总结/20201224贪心周末总结.md @@ -31,7 +31,7 @@ 把[贪心算法:用最少数量的箭引爆气球](https://mp.weixin.qq.com/s/HxVAJ6INMfNKiGwI88-RFw)代码稍做修改,别可以AC本题。 修改后的C++代码如下: -```C++ +```CPP class Solution { public: // 按照区间左边界从大到小排序 @@ -102,3 +102,4 @@ public: **「代码随想录」里总结的都是经典题目,大家跟着练就节省了不少选择题目的时间了**。 +
diff --git a/problems/周总结/20210107动规周末总结.md b/problems/周总结/20210107动规周末总结.md index 24700941..67957f05 100644 --- a/problems/周总结/20210107动规周末总结.md +++ b/problems/周总结/20210107动规周末总结.md @@ -51,7 +51,7 @@ dp[0]其实就是一个无意义的存在,不用去初始化dp[0]。 一个严谨的思考过程,应该是初始化dp[1] = 1,dp[2] = 2,然后i从3开始遍历,代码如下: -```C++ +```CPP dp[1] = 1; dp[2] = 2; for (int i = 3; i <= n; i++) { // 注意i是从3开始的 @@ -67,7 +67,7 @@ for (int i = 3; i <= n; i++) { // 注意i是从3开始的 这里我先给出我的实现代码: -```C++ +```CPP class Solution { public: int climbStairs(int n) { @@ -122,7 +122,7 @@ public: 所以代码也可以这么写: -```C++ +```CPP class Solution { public: int minCostClimbingStairs(vector& cost) { @@ -149,3 +149,4 @@ public: +
diff --git a/problems/周总结/20210114动规周末总结.md b/problems/周总结/20210114动规周末总结.md index acce0fb2..a49e0370 100644 --- a/problems/周总结/20210114动规周末总结.md +++ b/problems/周总结/20210114动规周末总结.md @@ -157,3 +157,4 @@ n为5时候的dp数组状态如图: 预告,我们下周正式开始讲解背包问题,经典的不能再经典,也是比较难的一类动态规划的题目了,录友们上车抓稳咯。 +
diff --git a/problems/周总结/20210121动规周末总结.md b/problems/周总结/20210121动规周末总结.md index dc0e7a46..bffe47fb 100644 --- a/problems/周总结/20210121动规周末总结.md +++ b/problems/周总结/20210121动规周末总结.md @@ -29,7 +29,7 @@ dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 3. dp数组如何初始化 -```C++ +```CPP // 初始化 dp vector> dp(weight.size() + 1, vector(bagWeight + 1, 0)); for (int j = bagWeight; j >= weight[0]; j--) { @@ -43,7 +43,7 @@ for (int j = bagWeight; j >= weight[0]; j--) { 但是先遍历物品更好理解。代码如下: -```C++ +```CPP // weight数组的大小 就是物品个数 for(int i = 1; i < weight.size(); i++) { // 遍历物品 for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量 @@ -107,7 +107,7 @@ dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); 代码如下: -```C++ +```CPP for(int i = 0; i < weight.size(); i++) { // 遍历物品 for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量 dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); @@ -158,3 +158,4 @@ for(int i = 0; i < weight.size(); i++) { // 遍历物品 就像是我们讲解01背包的时候,花了那么大力气才把每一个细节都讲清楚,这里其实是基础,后面的背包问题怎么变,基础比较牢固自然会有自己的一套思考过程。 +
diff --git a/problems/周总结/20210128动规周末总结.md b/problems/周总结/20210128动规周末总结.md index bd597e41..bea4cc60 100644 --- a/problems/周总结/20210128动规周末总结.md +++ b/problems/周总结/20210128动规周末总结.md @@ -86,7 +86,7 @@ dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1); 完全背包的物品是可以添加多次的,所以遍历背包容量要从小到大去遍历,即: -```C++ +```CPP // 先遍历物品,再遍历背包 for(int i = 0; i < weight.size(); i++) { // 遍历物品 for(int j = weight[i]; j < bagWeight ; j++) { // 遍历背包容量 @@ -139,3 +139,4 @@ for(int i = 0; i < weight.size(); i++) { // 遍历物品 此时相信大家对动规五部曲也有更深的理解了,同样也验证了Carl之前讲过的:**简单题是用来学习方法论的,而遇到难题才体现出方法论的重要性!** +
diff --git a/problems/周总结/20210204动规周末总结.md b/problems/周总结/20210204动规周末总结.md index db14f7f3..d2417f8f 100644 --- a/problems/周总结/20210204动规周末总结.md +++ b/problems/周总结/20210204动规周末总结.md @@ -20,7 +20,7 @@ 所以本题遍历顺序最终遍历顺序:**target(背包)放在外循环,将nums(物品)放在内循环,内循环从前到后遍历**。 -```C++ +```CPP class Solution { public: int combinationSum4(vector& nums, int target) { @@ -56,7 +56,7 @@ public: 和昨天的题目[动态规划:377. 组合总和 Ⅳ](https://mp.weixin.qq.com/s/Iixw0nahJWQgbqVNk8k6gA)基本就是一道题了,遍历顺序也是一样一样的! 代码如下: -```C++ +```CPP class Solution { public: int climbStairs(int n) { @@ -93,7 +93,7 @@ public: 外层for循环遍历物品,内层for遍历背包: -```C++ +```CPP // 版本一 class Solution { public: @@ -115,7 +115,7 @@ public: 外层for遍历背包,内层for循环遍历物品: -```C++ +```CPP // 版本二 class Solution { public: @@ -148,7 +148,7 @@ public: 先遍历背包,在遍历物品: -```C++ +```CPP // 版本一 class Solution { public: @@ -167,7 +167,7 @@ public: 先遍历物品,在遍历背包: -```C++ +```CPP // 版本二 class Solution { public: @@ -200,3 +200,4 @@ public: 此时我们就已经把完全背包的遍历顺序研究的透透的了! +
diff --git a/problems/周总结/20210225动规周末总结.md b/problems/周总结/20210225动规周末总结.md index 739d0469..ea3c5515 100644 --- a/problems/周总结/20210225动规周末总结.md +++ b/problems/周总结/20210225动规周末总结.md @@ -71,7 +71,7 @@ dp[1] = max(nums[0], nums[1]); 这道题目我给出了暴力的解法: -```C++ +```CPP class Solution { public: int rob(TreeNode* root) { @@ -94,7 +94,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: unordered_map umap; // 记录计算过的结果 @@ -120,7 +120,7 @@ public: 1. 确定递归函数的参数和返回值 -```C++ +```CPP vector robTree(TreeNode* cur) { ``` @@ -142,7 +142,7 @@ if (cur == NULL) return vector{0, 0}; 采用后序遍历,代码如下: -```C++ +```CPP // 下标0:不偷,下标1:偷 vector left = robTree(cur->left); // 左 vector right = robTree(cur->right); // 右 @@ -160,7 +160,7 @@ vector right = robTree(cur->right); // 右 代码如下: -```C++ +```CPP vector left = robTree(cur->left); // 左 vector right = robTree(cur->right); // 右 @@ -218,7 +218,7 @@ public: 因为股票就买卖一次,那么贪心的想法很自然就是取最左最小值,取最右最大值,那么得到的差值就是最大利润。 -```C++ +```CPP class Solution { public: int maxProfit(vector& prices) { @@ -237,7 +237,7 @@ public: 动规解法,版本一,代码如下: -```C++ +```CPP // 版本一 class Solution { public: @@ -262,7 +262,7 @@ public: 那么我们只需要记录 当前天的dp状态和前一天的dp状态就可以了,可以使用滚动数组来节省空间,代码如下: -```C++ +```CPP // 版本二 class Solution { public: @@ -300,3 +300,4 @@ public: **代码随想录温馨提醒:投资有风险,入市需谨慎!** +
diff --git a/problems/周总结/20210304动规周末总结.md b/problems/周总结/20210304动规周末总结.md index 977b41e0..9fea6244 100644 --- a/problems/周总结/20210304动规周末总结.md +++ b/problems/周总结/20210304动规周末总结.md @@ -110,7 +110,7 @@ j的状态表示为: 还要强调一下:dp[i][1],**表示的是第i天,买入股票的状态,并不是说一定要第i天买入股票,这是很多同学容易陷入的误区**。 -```C++ +```CPP for (int j = 0; j < 2 * k - 1; j += 2) { dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]); dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]); @@ -125,7 +125,7 @@ for (int j = 0; j < 2 * k - 1; j += 2) { 代码如下: -```C++ +```CPP for (int j = 1; j < 2 * k; j += 2) { dp[0][j] = -prices[0]; } @@ -202,3 +202,4 @@ vector> dp(n, vector(3, 0)); 下周还会有一篇股票系列的文章,**股票系列后面我也会单独写一篇总结,来高度概括一下,这样大家会对股票问题就有一个整体性的理解了**。 +
diff --git a/problems/周总结/二叉树阶段总结系列一.md b/problems/周总结/二叉树阶段总结系列一.md index dc73672d..c8235ff1 100644 --- a/problems/周总结/二叉树阶段总结系列一.md +++ b/problems/周总结/二叉树阶段总结系列一.md @@ -18,7 +18,7 @@ 对于二叉树节点的定义,C++代码如下: -```C++ +```CPP struct TreeNode { int val; TreeNode *left; @@ -40,7 +40,7 @@ TreeNode* a = new TreeNode(9); 没有构造函数的话就要这么写: -```C++ +```CPP TreeNode* a = new TreeNode(); a->val = 9; a->left = NULL; @@ -68,7 +68,7 @@ morris遍历是二叉树遍历算法的超强进阶算法,morris遍历可以 前序遍历空节点不入栈的代码:(注意注释部分,和文章中的区别) -```C++ +```CPP class Solution { public: vector preorderTraversal(TreeNode* root) { @@ -91,7 +91,7 @@ public: 后序遍历空节点不入栈的代码:(注意注释部分,和文章中的区别) -```C++ +```CPP class Solution { public: vector postorderTraversal(TreeNode* root) { @@ -152,7 +152,7 @@ public: 如果非要使用递归中序的方式写,也可以,如下代码就可以避免节点左右孩子翻转两次的情况: -```C++ +```CPP class Solution { public: TreeNode* invertTree(TreeNode* root) { @@ -171,7 +171,7 @@ public: 代码如下: -```C++ +```CPP class Solution { public: TreeNode* invertTree(TreeNode* root) { @@ -206,3 +206,4 @@ public: **本周我们都是讲解了二叉树,从理论基础到遍历方式,从递归到迭代,从深度遍历到广度遍历,最后再用了一个翻转二叉树的题目把我们之前讲过的遍历方式都串了起来。** +
diff --git a/problems/哈希表总结.md b/problems/哈希表总结.md index c3fbde2b..28865920 100644 --- a/problems/哈希表总结.md +++ b/problems/哈希表总结.md @@ -84,7 +84,7 @@ std::set和std::multiset底层实现都是红黑树,std::unordered_set的底 来说一说:使用数组和set来做哈希法的局限。 * 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。 -* set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下表位置,因为要返回x 和 y的下表。所以set 也不能用。 +* set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x 和 y的下标。所以set 也不能用。 map是一种``的结构,本题可以用key保存数值,用value在保存数值所在的下表。所以使用map最为合适。 @@ -131,4 +131,4 @@ std::unordered_map 底层实现为哈希,std::map 和std::multimap 的底层 * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/哈希表理论基础.md b/problems/哈希表理论基础.md index f78dc241..2d3b03bd 100644 --- a/problems/哈希表理论基础.md +++ b/problems/哈希表理论基础.md @@ -133,4 +133,4 @@ std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底 * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/回溯总结.md b/problems/回溯总结.md index 793df516..474db74a 100644 --- a/problems/回溯总结.md +++ b/problems/回溯总结.md @@ -454,4 +454,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/回溯算法去重问题的另一种写法.md b/problems/回溯算法去重问题的另一种写法.md index 19a4ae3b..cc563fb1 100644 --- a/problems/回溯算法去重问题的另一种写法.md +++ b/problems/回溯算法去重问题的另一种写法.md @@ -30,7 +30,7 @@ used数组去重版本: [回溯算法:求子集问题(二)](https://mp.w 使用set去重的版本如下: -```C++ +```CPP class Solution { private: vector> result; @@ -71,7 +71,7 @@ public: 例如: -```C++ +```CPP class Solution { private: vector> result; @@ -110,7 +110,7 @@ private: 代码如下: -```C++ +```CPP class Solution { private: vector> result; @@ -142,7 +142,7 @@ uset已经是全局变量,本层的uset记录了一个元素,然后进入下 使用set去重的版本如下: -```C++ +```CPP class Solution { private: vector> result; @@ -183,7 +183,7 @@ public: 使用set去重的版本如下: -```C++ +```CPP class Solution { private: vector> result; @@ -263,4 +263,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/回溯算法理论基础.md b/problems/回溯算法理论基础.md index 35e9db0f..b86c2506 100644 --- a/problems/回溯算法理论基础.md +++ b/problems/回溯算法理论基础.md @@ -181,4 +181,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/字符串总结.md b/problems/字符串总结.md index 71be6422..f9c9766e 100644 --- a/problems/字符串总结.md +++ b/problems/字符串总结.md @@ -130,4 +130,4 @@ KMP算法是字符串查找最重要的算法,但彻底理解KMP并不容易 * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/数组总结篇.md b/problems/数组总结篇.md index 2c679493..009cc6f2 100644 --- a/problems/数组总结篇.md +++ b/problems/数组总结篇.md @@ -149,4 +149,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/数组理论基础.md b/problems/数组理论基础.md index b2837375..6d7b9f9a 100644 --- a/problems/数组理论基础.md +++ b/problems/数组理论基础.md @@ -53,7 +53,7 @@ 我们来做一个实验,C++测试代码如下: -```C++ +```CPP void test_arr() { int array[2][3] = { {0, 1, 2}, @@ -78,7 +78,7 @@ int main() { 注意地址为16进制,可以看出二维数组地址是连续一条线的。 -一些录友可能看不懂内存地址,我就简单介绍一下, 0x7ffee4065820 与 0x7ffee4065824 差了一个4,就是4个字节,因为这是一个int型的数组,所以两个相信数组元素地址差4个字节。 +一些录友可能看不懂内存地址,我就简单介绍一下, 0x7ffee4065820 与 0x7ffee4065824 差了一个4,就是4个字节,因为这是一个int型的数组,所以两个相邻数组元素地址差4个字节。 0x7ffee4065828 与 0x7ffee406582c 也是差了4个字节,在16进制里8 + 4 = c,c就是12。 @@ -124,4 +124,4 @@ public static void test_arr() { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/栈与队列总结.md b/problems/栈与队列总结.md index b3bb5c47..70ef7e9c 100644 --- a/problems/栈与队列总结.md +++ b/problems/栈与队列总结.md @@ -181,4 +181,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/栈与队列理论基础.md b/problems/栈与队列理论基础.md index db871a3c..c43ce0f5 100644 --- a/problems/栈与队列理论基础.md +++ b/problems/栈与队列理论基础.md @@ -94,4 +94,4 @@ std::queue> third; // 定义以list为底层容器的队列 * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/根据身高重建队列(vector原理讲解).md b/problems/根据身高重建队列(vector原理讲解).md index c548f6d8..baf7dcf5 100644 --- a/problems/根据身高重建队列(vector原理讲解).md +++ b/problems/根据身高重建队列(vector原理讲解).md @@ -12,7 +12,7 @@ 但是在解释的过程中有不恰当的地方,所以来专门写一篇文章来详细说一说这个问题。 使用vector的代码如下: -```C++ +```CPP // 版本一,使用vector(动态数组) class Solution { public: @@ -38,7 +38,7 @@ public: 其直观上来看数组的insert操作是O(n)的,整体代码的时间复杂度是O(n^2)。 这么一分析好像和版本二链表实现的时间复杂度是一样的啊,为什么提交之后效率会差距这么大呢? -```C++ +```CPP // 版本二,使用list(链表) class Solution { public: @@ -107,7 +107,7 @@ for (int i = 0; i < vec.size(); i++) { 手动模拟的过程其实不是很简单的,需要很多细节,我粗略写了一个版本,如下: -```C++ +```CPP // 版本三 // 使用vector,但不让它动态扩容 class Solution { @@ -180,4 +180,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/算法模板.md b/problems/算法模板.md index 888ae773..8f6b053e 100644 --- a/problems/算法模板.md +++ b/problems/算法模板.md @@ -296,4 +296,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/背包总结篇.md b/problems/背包总结篇.md index 0dd407ac..7ec2d6bf 100644 --- a/problems/背包总结篇.md +++ b/problems/背包总结篇.md @@ -101,4 +101,4 @@ * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/背包理论基础01背包-1.md b/problems/背包理论基础01背包-1.md index 3f603366..c13efc3a 100644 --- a/problems/背包理论基础01背包-1.md +++ b/problems/背包理论基础01背包-1.md @@ -82,8 +82,8 @@ leetcode上没有纯01背包的问题,都是01背包应用方面的题目, 那么可以有两个方向推出来dp[i][j], -* 由dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以被背包内的价值依然和前面相同。) -* 由dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,那么dp[i - 1][j - weight[i]] + value[i] (物品i的价值),就是背包放物品i得到的最大价值 +* **不放物品i**:由dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以被背包内的价值依然和前面相同。) +* **放物品i**:由dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,那么dp[i - 1][j - weight[i]] + value[i] (物品i的价值),就是背包放物品i得到的最大价值 所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); @@ -223,7 +223,7 @@ dp[i-1][j]和dp[i - 1][j - weight[i]] 都在dp[i][j]的左上角方向(包括 ## 完整C++测试代码 -```C++ +```CPP void test_2_wei_bag_problem1() { vector weight = {1, 3, 4}; vector value = {15, 20, 30}; @@ -434,4 +434,4 @@ test(); * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/背包理论基础01背包-2.md b/problems/背包理论基础01背包-2.md index ee2fb6d7..4f1455e9 100644 --- a/problems/背包理论基础01背包-2.md +++ b/problems/背包理论基础01背包-2.md @@ -60,7 +60,7 @@ dp[j]可以通过dp[j - weight[i]]推导出来,dp[j - weight[i]]表示容量 dp[j - weight[i]] + value[i] 表示 容量为 j - 物品i重量 的背包 加上 物品i的价值。(也就是容量为j的背包,放入物品i了之后的价值即:dp[j]) -此时dp[j]有两个选择,一个是取自己dp[j],一个是取dp[j - weight[i]] + value[i],指定是取最大的,毕竟是求最大价值, +此时dp[j]有两个选择,一个是取自己dp[j] 相当于 二维dp数组中的dp[i-1][j],即不放物品i,一个是取dp[j - weight[i]] + value[i],即放物品i,指定是取最大的,毕竟是求最大价值, 所以递归公式为: @@ -153,7 +153,7 @@ dp[1] = dp[1 - weight[0]] + value[0] = 15 ## 一维dp01背包完整C++测试代码 -```C++ +```CPP void test_1_wei_bag_problem() { vector weight = {1, 3, 4}; vector value = {15, 20, 30}; @@ -323,4 +323,4 @@ test(); * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/背包问题理论基础多重背包.md b/problems/背包问题理论基础多重背包.md index e14575d4..85cbf4de 100644 --- a/problems/背包问题理论基础多重背包.md +++ b/problems/背包问题理论基础多重背包.md @@ -56,7 +56,7 @@ 这种方式来实现多重背包的代码如下: -```C++ +```CPP void test_multi_pack() { vector weight = {1, 3, 4}; vector value = {15, 20, 30}; @@ -96,7 +96,7 @@ int main() { 代码如下:(详看注释) -```C++ +```CPP void test_multi_pack() { vector weight = {1, 3, 4}; vector value = {15, 20, 30}; @@ -207,4 +207,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/背包问题理论基础完全背包.md b/problems/背包问题理论基础完全背包.md index 1ad09c4b..dc4f06f9 100644 --- a/problems/背包问题理论基础完全背包.md +++ b/problems/背包问题理论基础完全背包.md @@ -51,7 +51,7 @@ for(int i = 0; i < weight.size(); i++) { // 遍历物品 而完全背包的物品是可以添加多次的,所以要从小到大去遍历,即: -```C++ +```CPP // 先遍历物品,再遍历背包 for(int i = 0; i < weight.size(); i++) { // 遍历物品 for(int j = weight[i]; j < bagWeight ; j++) { // 遍历背包容量 @@ -92,13 +92,13 @@ dp状态图如下: 遍历背包容量在外层循环,遍历物品在内层循环,状态如图: -![动态规划-完全背包2](https://img-blog.csdnimg.cn/20210126104741304.jpg) +![动态规划-完全背包2](https://code-thinking-1253855093.file.myqcloud.com/pics/20210729234011.png) 看了这两个图,大家就会理解,完全背包中,两个for循环的先后循序,都不影响计算dp[j]所需要的值(这个值就是下标j之前所对应的dp[j])。 先遍历被背包在遍历物品,代码如下: -```C++ +```CPP // 先遍历背包,再遍历物品 for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量 for(int i = 0; i < weight.size(); i++) { // 遍历物品 @@ -112,7 +112,7 @@ for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量 完整的C++测试代码如下: -```C++ +```CPP // 先遍历物品,在遍历背包 void test_CompletePack() { vector weight = {1, 3, 4}; @@ -132,7 +132,7 @@ int main() { ``` -```C++ +```CPP // 先遍历背包,再遍历物品 void test_CompletePack() { @@ -311,11 +311,42 @@ func main() { fmt.Println(test_CompletePack2(weight, price, 4)) } ``` +Javascript: +```Javascript +// 先遍历物品,再遍历背包容量 +function test_completePack1() { + let weight = [1, 3, 5] + let value = [15, 20, 30] + let bagWeight = 4 + let dp = new Array(bagWeight + 1).fill(0) + for(let i = 0; i <= weight.length; i++) { + for(let j = weight[i]; j <= bagWeight; j++) { + dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]) + } + } + console.log(dp) +} +// 先遍历背包容量,再遍历物品 +function test_completePack2() { + let weight = [1, 3, 5] + let value = [15, 20, 30] + let bagWeight = 4 + let dp = new Array(bagWeight + 1).fill(0) + for(let j = 0; j <= bagWeight; j++) { + for(let i = 0; i < weight.length; i++) { + if (j >= weight[i]) { + dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]) + } + } + } + console.log(2, dp); +} +``` ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/贪心算法总结篇.md b/problems/贪心算法总结篇.md index 999797ad..413eb3ac 100644 --- a/problems/贪心算法总结篇.md +++ b/problems/贪心算法总结篇.md @@ -159,4 +159,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/贪心算法理论基础.md b/problems/贪心算法理论基础.md index 5385aa60..77e8fe95 100644 --- a/problems/贪心算法理论基础.md +++ b/problems/贪心算法理论基础.md @@ -108,4 +108,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/链表总结篇.md b/problems/链表总结篇.md index 6404dd6e..1f0de6fb 100644 --- a/problems/链表总结篇.md +++ b/problems/链表总结篇.md @@ -98,4 +98,4 @@ * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/链表理论基础.md b/problems/链表理论基础.md index 252247c7..d210b6bd 100644 --- a/problems/链表理论基础.md +++ b/problems/链表理论基础.md @@ -159,4 +159,4 @@ Go: * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+
diff --git a/problems/面试题02.07.链表相交.md b/problems/面试题02.07.链表相交.md index c6779427..8c3a5831 100644 --- a/problems/面试题02.07.链表相交.md +++ b/problems/面试题02.07.链表相交.md @@ -44,7 +44,7 @@ C++代码如下: -```C++ +```CPP class Solution { public: ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { @@ -160,34 +160,21 @@ Python: class Solution: def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode: - lengthA,lengthB = 0,0 - curA,curB = headA,headB - while(curA!=None): #求链表A的长度 - curA = curA.next - lengthA +=1 - - while(curB!=None): #求链表B的长度 - curB = curB.next - lengthB +=1 - - curA, curB = headA, headB + """ + 根据快慢法则,走的快的一定会追上走得慢的。 + 在这道题里,有的链表短,他走完了就去走另一条链表,我们可以理解为走的快的指针。 - if lengthB>lengthA: #让curA为最长链表的头,lenA为其长度 - lengthA, lengthB = lengthB, lengthA - curA, curB = curB, curA + 那么,只要其中一个链表走完了,就去走另一条链表的路。如果有交点,他们最终一定会在同一个 + 位置相遇 + """ + cur_a, cur_b = headA, headB # 用两个指针代替a和b - gap = lengthA - lengthB #求长度差 - while(gap!=0): - curA = curA.next #让curA和curB在同一起点上 - gap -= 1 - while(curA!=None): - if curA == curB: - return curA - else: - curA = curA.next - curB = curB.next - return None + while cur_a != cur_b: + cur_a = cur_a.next if cur_a else headB # 如果a走完了,那么就切换到b走 + cur_b = cur_b.next if cur_b else headA # 同理,b走完了就切换到a + + return cur_a ``` Go: @@ -271,4 +258,4 @@ var getIntersectionNode = function(headA, headB) { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
+