diff --git a/problems/0019.删除链表的倒数第N个节点.md b/problems/0019.删除链表的倒数第N个节点.md index 00caeea0..a84c0b29 100644 --- a/problems/0019.删除链表的倒数第N个节点.md +++ b/problems/0019.删除链表的倒数第N个节点.md @@ -87,30 +87,27 @@ public: java: ```java -class Solution { - public ListNode removeNthFromEnd(ListNode head, int n) { - ListNode dummy = new ListNode(-1); - dummy.next = head; +public ListNode removeNthFromEnd(ListNode head, int n){ + ListNode dummyNode = new ListNode(0); + dummyNode.next = head; - ListNode slow = dummy; - ListNode fast = dummy; - while (n-- > 0) { - fast = fast.next; - } - // 记住 待删除节点slow 的上一节点 - ListNode prev = null; - while (fast != null) { - prev = slow; - slow = slow.next; - fast = fast.next; - } - // 上一节点的next指针绕过 待删除节点slow 直接指向slow的下一节点 - prev.next = slow.next; - // 释放 待删除节点slow 的next指针, 这句删掉也能AC - slow.next = null; + ListNode fastIndex = dummyNode; + ListNode slowIndex = dummyNode; - return dummy.next; + //只要快慢指针相差 n 个结点即可 + for (int i = 0; i < n ; i++){ + fastIndex = fastIndex.next; } + + while (fastIndex.next != null){ + fastIndex = fastIndex.next; + slowIndex = slowIndex.next; + } + + //此时 slowIndex 的位置就是待删除元素的前一个位置。 + //具体情况可自己画一个链表长度为 3 的图来模拟代码来理解 + slowIndex.next = slowIndex.next.next; + return dummyNode.next; } ``` diff --git a/problems/0045.跳跃游戏II.md b/problems/0045.跳跃游戏II.md index 13142c99..59e30df5 100644 --- a/problems/0045.跳跃游戏II.md +++ b/problems/0045.跳跃游戏II.md @@ -214,7 +214,26 @@ class Solution: return ans ``` +```python +# 贪心版本二 +class Solution: + def jump(self, nums: List[int]) -> int: + if len(nums) == 1: + return 0 + curDistance, nextDistance = 0, 0 + step = 0 + for i in range(len(nums)-1): + nextDistance = max(nextDistance, nums[i]+i) + if i == curDistance: + curDistance = nextDistance + step += 1 + return step +``` + + + ### Go + ```Go func jump(nums []int) int { dp := make([]int, len(nums)) @@ -240,7 +259,71 @@ func min(a, b int) int { } ``` +```go +// 贪心版本一 +func jump(nums []int) int { + n := len(nums) + if n == 1 { + return 0 + } + cur, next := 0, 0 + step := 0 + for i := 0; i < n; i++ { + next = max(nums[i]+i, next) + if i == cur { + if cur != n-1 { + step++ + cur = next + if cur >= n-1 { + return step + } + } else { + return step + } + } + } + return step +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```go +// 贪心版本二 +func jump(nums []int) int { + n := len(nums) + if n == 1 { + return 0 + } + cur, next := 0, 0 + step := 0 + for i := 0; i < n-1; i++ { + next = max(nums[i]+i, next) + if i == cur { + cur = next + step++ + } + } + return step +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + + + ### Javascript + ```Javascript var jump = function(nums) { let curIndex = 0 diff --git a/problems/0055.跳跃游戏.md b/problems/0055.跳跃游戏.md index 394117ee..f2bcedcc 100644 --- a/problems/0055.跳跃游戏.md +++ b/problems/0055.跳跃游戏.md @@ -139,7 +139,31 @@ func canJUmp(nums []int) bool { } ``` +```go +// 贪心 +func canJump(nums []int) bool { + cover := 0 + n := len(nums)-1 + for i := 0; i <= cover; i++ { // 每次与覆盖值比较 + cover = max(i+nums[i], cover) //每走一步都将 cover 更新为最大值 + if cover >= n { + return true + } + } + return false +} +func max(a, b int ) int { + if a > b { + return a + } + return b +} +``` + + + ### Javascript + ```Javascript var canJump = function(nums) { if(nums.length === 1) return true diff --git a/problems/0104.二叉树的最大深度.md b/problems/0104.二叉树的最大深度.md index 2bcc9c5c..a7c206af 100644 --- a/problems/0104.二叉树的最大深度.md +++ b/problems/0104.二叉树的最大深度.md @@ -313,6 +313,31 @@ class solution { } ``` +```java +class Solution { + /** + * 递归法(求深度法) + */ + //定义最大深度 + int maxnum = 0; + + public int maxDepth(TreeNode root) { + ans(root,0); + return maxnum; + } + + //递归求解最大深度 + void ans(TreeNode tr,int tmp){ + if(tr==null) return; + tmp++; + maxnum = maxnum 0 { + n := q.Len() + for i := 0; i < n; i++ { + node := q.Remove(q.Front()).(*Node) + for j := range node.Children { + q.PushBack(node.Children[j]) + } + } + depth++ + } + return depth +} +``` ## javascript diff --git a/problems/0106.从中序与后序遍历序列构造二叉树.md b/problems/0106.从中序与后序遍历序列构造二叉树.md index 92eb9a25..0fd50897 100644 --- a/problems/0106.从中序与后序遍历序列构造二叉树.md +++ b/problems/0106.从中序与后序遍历序列构造二叉树.md @@ -27,7 +27,7 @@ ![106. 从中序与后序遍历序列构造二叉树1](https://img-blog.csdnimg.cn/20210203154316774.png) -## 视频讲解 +# 视频讲解 **《代码随想录》算法视频公开课:[坑很多!来看看你掉过几次坑 | LeetCode:106.从中序与后序遍历序列构造二叉树](https://www.bilibili.com/video/BV1vW4y1i7dn),相信结合视频在看本篇题解,更有助于大家对本题的理解**。 diff --git a/problems/0142.环形链表II.md b/problems/0142.环形链表II.md index 2054fd35..050b5ee4 100644 --- a/problems/0142.环形链表II.md +++ b/problems/0142.环形链表II.md @@ -68,7 +68,7 @@ fast和slow各自再走一步, fast和slow就相遇了 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。 如图所示: -![142环形链表2](https://img-blog.csdnimg.cn/20210318162938397.png) +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20220925103433.png) 那么相遇时: slow指针走过的节点数为: `x + y`, diff --git a/problems/0150.逆波兰表达式求值.md b/problems/0150.逆波兰表达式求值.md index 8107e4e0..13b2ba46 100644 --- a/problems/0150.逆波兰表达式求值.md +++ b/problems/0150.逆波兰表达式求值.md @@ -98,7 +98,7 @@ public: st.pop(); if (tokens[i] == "+") st.push(num2 + num1); if (tokens[i] == "-") st.push(num2 - num1); - if (tokens[i] == "*") st.push(num2 * num1); + if (tokens[i] == "*") st.push((long)num2 * (long)num1); //力扣改了后台测试数据 if (tokens[i] == "/") st.push(num2 / num1); } else { st.push(stoi(tokens[i])); diff --git a/problems/0222.完全二叉树的节点个数.md b/problems/0222.完全二叉树的节点个数.md index dc037f0a..eecc4dc1 100644 --- a/problems/0222.完全二叉树的节点个数.md +++ b/problems/0222.完全二叉树的节点个数.md @@ -437,6 +437,33 @@ func countNodes(root *TreeNode) int { } ``` +迭代法 + +```go +func countNodes(root *TreeNode) int { + if root == nil { + return 0 + } + q := list.New() + q.PushBack(root) + res := 0 + for q.Len() > 0 { + n := q.Len() + for i := 0; i < n; i++ { + node := q.Remove(q.Front()).(*TreeNode) + if node.Left != nil { + q.PushBack(node.Left) + } + if node.Right != nil { + q.PushBack(node.Right) + } + res++ + } + } + return res +} +``` + ## JavaScript: diff --git a/problems/0235.二叉搜索树的最近公共祖先.md b/problems/0235.二叉搜索树的最近公共祖先.md index ee86d02f..07e55ba5 100644 --- a/problems/0235.二叉搜索树的最近公共祖先.md +++ b/problems/0235.二叉搜索树的最近公共祖先.md @@ -38,24 +38,32 @@ # 思路 - 做过[二叉树:公共祖先问题](https://programmercarl.com/0236.二叉树的最近公共祖先.html)题目的同学应该知道,利用回溯从底向上搜索,遇到一个节点的左子树里有p,右子树里有q,那么当前节点就是最近公共祖先。 那么本题是二叉搜索树,二叉搜索树是有序的,那得好好利用一下这个特点。 在有序树里,如果判断一个节点的左子树里有p,右子树里有q呢? -其实只要从上到下遍历的时候,cur节点是数值在[p, q]区间中则说明该节点cur就是最近公共祖先了。 +因为是有序树,所有 如果 中间节点是 q 和 p 的公共祖先,那么 中节点的数组 一定是在 [p, q]区间的。即 中节点 > p && 中节点 < q 或者 中节点 > q && 中节点 < p。 + +那么只要从上到下去遍历,遇到 cur节点是数值在[p, q]区间中则一定可以说明该节点cur就是q 和 p的公共祖先。 那问题来了,**一定是最近公共祖先吗**? + +如图,我们从根节点搜索,第一次遇到 cur节点是数值在[p, q]区间中,即 节点5,此时可以说明 p 和 q 一定分别存在于 节点 5的左子树,和右子树中。 + +![235.二叉搜索树的最近公共祖先](https://code-thinking-1253855093.file.myqcloud.com/pics/20220926164214.png) + +此时节点5是不是最近公共祖先? 如果 从节点5继续向左遍历,那么将错过成为q的祖先, 如果从节点5继续向右遍历则错过成为p的祖先。 + +所以当我们从上向下去递归遍历,第一次遇到 cur节点是数值在[p, q]区间中,那么cur就是 p和q的最近公共祖先。 理解这一点,本题就很好解了。 -和[二叉树:公共祖先问题](https://programmercarl.com/0236.二叉树的最近公共祖先.html)不同,普通二叉树求最近公共祖先需要使用回溯,从底向上来查找,二叉搜索树就不用了,因为搜索树有序(相当于自带方向),那么只要从上向下遍历就可以了。 - -那么我们可以采用前序遍历(其实这里没有中节点的处理逻辑,遍历顺序无所谓了)。 +而递归遍历顺序,本题就不涉及到 前中后序了(这里没有中节点的处理逻辑,遍历顺序无所谓了)。 如图所示:p为节点3,q为节点5 -![235.二叉搜索树的最近公共祖先](https://img-blog.csdnimg.cn/20210204150858927.png) +![235.二叉搜索树的最近公共祖先2](https://code-thinking-1253855093.file.myqcloud.com/pics/20220926165141.png) + 可以看出直接按照指定的方向,就可以找到节点4,为最近公共祖先,而且不需要遍历整棵树,找到结果直接返回! diff --git a/problems/0236.二叉树的最近公共祖先.md b/problems/0236.二叉树的最近公共祖先.md index bae9547c..82ca09f0 100644 --- a/problems/0236.二叉树的最近公共祖先.md +++ b/problems/0236.二叉树的最近公共祖先.md @@ -41,23 +41,29 @@ 回溯啊,二叉树回溯的过程就是从低到上。 -后序遍历就是天然的回溯过程,最先处理的一定是叶子节点。 +后序遍历(左右中)就是天然的回溯过程,可以根据左右子树的返回值,来处理中节点的逻辑。 接下来就看如何判断一个节点是节点q和节点p的公共公共祖先呢。 -**首先最容易想到的一个情况:如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。** +**首先最容易想到的一个情况:如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。** 即情况一: -**但是很多人容易忽略一个情况,就是节点本身p(q),它拥有一个子孙节点q(p)。** +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20220922173502.png) -使用后序遍历,回溯的过程,就是从低向上遍历节点,一旦发现满足第一种情况的节点,就是最近公共节点了。 +判断逻辑是 如果递归遍历遇到q,就将q返回,遇到p 就将p返回,那么如果 左右子树的返回值都不为空,说明此时的中节点,一定是q 和p 的最近祖先。 -**但是如果p或者q本身就是最近公共祖先呢**? +那么有录友可能疑惑,会不会左子树 遇到q 返回,右子树也遇到q返回,这样并没有找到 q 和p的最近祖先。 -其实只需要找到一个节点是p或者q的时候,直接返回当前节点,无需继续递归子树。 +这么想的录友,要审题了,题目强调:**二叉树节点数值是不重复的,而且一定存在 q 和 p**。 -如果接下来的遍历中找到了后继节点满足第一种情况则修改返回值为后继节点,否则,继续返回已找到的节点即可。 +**但是很多人容易忽略一个情况,就是节点本身p(q),它拥有一个子孙节点q(p)。** 情况二: -为什么满足第一种情况的节点一定是p或q的后继节点呢?大家可以仔细思考一下。 +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20220922173530.png) + +其实情况一 和 情况二 代码实现过程都是一样的,也可以说,实现情况一的逻辑,顺便包含了情况二。 + +因为遇到 q 或者 p 就返回,这样也包含了 q 或者 p 本省就是 公共祖先的情况。 + +这一点是很多录友容易忽略的,在下面的代码讲解中,可以在去体会。 递归三部曲: @@ -69,20 +75,24 @@ 代码如下: -``` +```CPP TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) ``` * 确定终止条件 -如果找到了 节点p或者q,或者遇到空节点,就返回。 +遇到空的话,然后然后空,因为树都是空了,所以返回空。 + +那么我们来说一说,如果 root == q,或者 root == p,说明找到 q p ,则将其返回,这个返回值,后面在中节点的处理过程中会用到,那么中节点处理逻辑,后下面讲解。 代码如下: -``` +```CPP if (root == q || root == p || root == NULL) return root; ``` + + * 确定单层递归逻辑 值得注意的是 本题函数有返回值,是因为回溯的过程需要递归函数的返回值做判断,但本题我们依然要遍历树的所有节点。 @@ -93,7 +103,7 @@ if (root == q || root == p || root == NULL) return root; 搜索一条边的写法: -``` +```CPP if (递归函数(root->left)) return ; if (递归函数(root->right)) return ; @@ -101,10 +111,10 @@ if (递归函数(root->right)) return ; 搜索整个树写法: -``` -left = 递归函数(root->left); -right = 递归函数(root->right); -left与right的逻辑处理; +```CPP +left = 递归函数(root->left); // 左 +right = 递归函数(root->right); // 右 +left与right的逻辑处理; // 中 ``` 看出区别了没? @@ -123,10 +133,10 @@ left与right的逻辑处理; 因为在如下代码的后序遍历中,如果想利用left和right做逻辑处理, 不能立刻返回,而是要等left与right逻辑处理完之后才能返回。 -``` -left = 递归函数(root->left); -right = 递归函数(root->right); -left与right的逻辑处理; +```CPP +left = 递归函数(root->left); // 左 +right = 递归函数(root->right); // 右 +left与right的逻辑处理; // 中 ``` 所以此时大家要知道我们要遍历整棵树。知道这一点,对本题就有一定深度的理解了。 @@ -134,7 +144,7 @@ left与right的逻辑处理; 那么先用left和right接住左子树和右子树的返回值,代码如下: -``` +```CPP TreeNode* left = lowestCommonAncestor(root->left, p, q); TreeNode* right = lowestCommonAncestor(root->right, p, q); diff --git a/problems/0617.合并二叉树.md b/problems/0617.合并二叉树.md index 6a843763..eb485afc 100644 --- a/problems/0617.合并二叉树.md +++ b/problems/0617.合并二叉树.md @@ -19,13 +19,18 @@ 注意: 合并必须从两个树的根节点开始。 -# 思路 +# 视频讲解 + +**《代码随想录》算法视频公开课:[一起操作两个二叉树?有点懵!| LeetCode:617.合并二叉树](https://www.bilibili.com/video/BV1m14y1Y7JK),相信结合视频在看本篇题解,更有助于大家对本题的理解**。 + + +## 思路 相信这道题目很多同学疑惑的点是如何同时遍历两个二叉树呢? 其实和遍历一个树逻辑是一样的,只不过传入两个树的节点,同时操作。 -## 递归 +### 递归 二叉树使用递归,就要想使用前中后哪种遍历方式? @@ -207,7 +212,7 @@ public: }; ``` -# 拓展 +## 拓展 当然也可以秀一波指针的操作,这是我写的野路子,大家就随便看看就行了,以防带跑遍了。 @@ -239,7 +244,7 @@ public: }; ``` -# 总结 +## 总结 合并二叉树,也是二叉树操作的经典题目,如果没有接触过的话,其实并不简单,因为我们习惯了操作一个二叉树,一起操作两个二叉树,还会有点懵懵的。 @@ -250,10 +255,10 @@ public: 最后拓展中,我给了一个操作指针的野路子,大家随便看看就行了,如果学习C++的话,可以在去研究研究。 -# 其他语言版本 +## 其他语言版本 -## Java +### Java ```Java class Solution { @@ -346,7 +351,7 @@ class Solution { } ``` -## Python +### Python **递归法 - 前序遍历** ```python @@ -409,7 +414,7 @@ class Solution: return root1 ``` -## Go +### Go ```go /** @@ -503,7 +508,7 @@ func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode { } ``` -## JavaScript +### JavaScript > 递归法: @@ -583,11 +588,11 @@ var mergeTrees = function(root1, root2) { ``` -## TypeScript +### TypeScript > 递归法: -```type +```typescript function mergeTrees(root1: TreeNode | null, root2: TreeNode | null): TreeNode | null { if (root1 === null) return root2; if (root2 === null) return root1; @@ -631,7 +636,7 @@ function mergeTrees(root1: TreeNode | null, root2: TreeNode | null): TreeNode | }; ``` -## Scala +### Scala 递归: ```scala diff --git a/problems/0654.最大二叉树.md b/problems/0654.最大二叉树.md index e2a64bcd..18739fdf 100644 --- a/problems/0654.最大二叉树.md +++ b/problems/0654.最大二叉树.md @@ -25,7 +25,12 @@ 给定的数组的大小在 [1, 1000] 之间。 -# 思路 +## 视频讲解 + +**《代码随想录》算法视频公开课:[又是构造二叉树,又有很多坑!| LeetCode:654.最大二叉树](https://www.bilibili.com/video/BV1MG411G7ox),相信结合视频在看本篇题解,更有助于大家对本题的理解**。 + + +## 思路 最大二叉树的构建过程如下: @@ -175,7 +180,7 @@ public: }; ``` -# 拓展 +## 拓展 可以发现上面的代码看上去简洁一些,**主要是因为第二版其实是允许空节点进入递归,所以不用在递归的时候加判断节点是否为空** @@ -207,7 +212,7 @@ root->right = traversal(nums, maxValueIndex + 1, right); 第二版相应的终止条件,是遇到空节点,也就是数组区间为0,就终止了。 -# 总结 +## 总结 这道题目其实和 [二叉树:构造二叉树登场!](https://programmercarl.com/0106.从中序与后序遍历序列构造二叉树.html) 是一个思路,比[二叉树:构造二叉树登场!](https://programmercarl.com/0106.从中序与后序遍历序列构造二叉树.html) 还简单一些。 @@ -218,10 +223,10 @@ root->right = traversal(nums, maxValueIndex + 1, right); 其实就是不同代码风格的实现,**一般情况来说:如果让空节点(空指针)进入递归,就不加if,如果不让空节点进入递归,就加if限制一下, 终止条件也会相应的调整。** -# 其他语言版本 +## 其他语言版本 -## Java +### Java ```Java class Solution { @@ -253,7 +258,7 @@ class Solution { } ``` -## Python +### Python ```python class Solution: @@ -300,7 +305,7 @@ class Solution: return root ``` -## Go +### Go ```go @@ -334,7 +339,7 @@ func findMax(nums []int) (index int){ } ``` -## JavaScript +### JavaScript ```javascript /** @@ -371,7 +376,7 @@ var constructMaximumBinaryTree = function (nums) { }; ``` -## TypeScript +### TypeScript > 新建数组法: @@ -419,7 +424,7 @@ function constructMaximumBinaryTree(nums: number[]): TreeNode | null { -## C +### C ```c struct TreeNode* traversal(int* nums, int left, int right) { @@ -450,7 +455,7 @@ struct TreeNode* constructMaximumBinaryTree(int* nums, int numsSize){ } ``` -## Swift +### Swift ```swift func constructMaximumBinaryTree(_ nums: inout [Int]) -> TreeNode? { return traversal(&nums, 0, nums.count) @@ -476,7 +481,7 @@ func traversal(_ nums: inout [Int], _ left: Int, _ right: Int) -> TreeNode? { } ``` -## Scala +### Scala ```scala object Solution { diff --git a/problems/0700.二叉搜索树中的搜索.md b/problems/0700.二叉搜索树中的搜索.md index 22f009cb..53c9136e 100644 --- a/problems/0700.二叉搜索树中的搜索.md +++ b/problems/0700.二叉搜索树中的搜索.md @@ -17,7 +17,12 @@ 在上述示例中,如果要找的值是 5,但因为没有节点值为 5,我们应该返回 NULL。 -# 思路 +# 视频讲解 + +**《代码随想录》算法视频公开课:[不愧是搜索树,这次搜索有方向了!| LeetCode:700.二叉搜索树中的搜索](https://www.bilibili.com/video/BV1wG411g7sF),相信结合视频在看本篇题解,更有助于大家对本题的理解**。 + + +## 思路 之前我们讲了都是普通二叉树,那么接下来看看二叉搜索树。 @@ -33,7 +38,7 @@ 本题,其实就是在二叉搜索树中搜索一个节点。那么我们来看看应该如何遍历。 -## 递归法 +### 递归法 1. 确定递归函数的参数和返回值 @@ -106,7 +111,7 @@ public: ``` -## 迭代法 +### 迭代法 一提到二叉树遍历的迭代法,可能立刻想起使用栈来模拟深度遍历,使用队列来模拟广度遍历。 @@ -140,7 +145,7 @@ public: 第一次看到了如此简单的迭代法,是不是感动的痛哭流涕,哭一会~ -# 总结 +## 总结 本篇我们介绍了二叉搜索树的遍历方式,因为二叉搜索树的有序性,遍历的时候要比普通二叉树简单很多。 @@ -153,9 +158,9 @@ public: -# 其他语言版本 +## 其他语言版本 -## Java +### Java ```Java class Solution { @@ -222,7 +227,7 @@ class Solution { } ``` -## Python +### Python 递归法: @@ -257,7 +262,7 @@ class Solution: ``` -## Go +### Go 递归法: @@ -292,7 +297,7 @@ func searchBST(root *TreeNode, val int) *TreeNode { } ``` -## JavaScript +### JavaScript 递归: @@ -350,7 +355,7 @@ var searchBST = function (root, val) { }; ``` -## TypeScript +### TypeScript > 递归法 @@ -380,7 +385,7 @@ function searchBST(root: TreeNode | null, val: number): TreeNode | null { }; ``` -## Scala +### Scala 递归: ```scala diff --git a/problems/链表理论基础.md b/problems/链表理论基础.md index 8378f7f2..cdd861fd 100644 --- a/problems/链表理论基础.md +++ b/problems/链表理论基础.md @@ -218,6 +218,22 @@ class ListNode(_x: Int = 0, _next: ListNode = null) { } ``` +Rust: +```rust +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct ListNode { + pub val: T, + pub next: Option>>, +} + +impl ListNode { + #[inline] + fn new(val: T, node: Option>>) -> Self { + ListNode { next: node, val } + } +} +``` + -----------------------