From a64f7a1734e676d18f2458a43fd732d8a6d101aa Mon Sep 17 00:00:00 2001 From: programmercarl <826123027@qq.com> Date: Fri, 16 Sep 2022 09:40:24 +0800 Subject: [PATCH] Update --- problems/0098.验证二叉搜索树.md | 10 +- problems/0104.二叉树的最大深度.md | 15 ++- problems/0110.平衡二叉树.md | 28 +++--- problems/0111.二叉树的最小深度.md | 3 + problems/0112.路径总和.md | 24 ++--- .../0222.完全二叉树的节点个数.md | 2 + problems/0257.二叉树的所有路径.md | 2 + problems/0404.左叶子之和.md | 3 + problems/0501.二叉搜索树中的众数.md | 8 +- problems/0513.找树左下角的值.md | 91 +++++++++---------- .../0530.二叉搜索树的最小绝对差.md | 2 +- problems/0700.二叉搜索树中的搜索.md | 35 +++++-- 12 files changed, 124 insertions(+), 99 deletions(-) diff --git a/problems/0098.验证二叉搜索树.md b/problems/0098.验证二叉搜索树.md index cba450e5..727144d5 100644 --- a/problems/0098.验证二叉搜索树.md +++ b/problems/0098.验证二叉搜索树.md @@ -87,7 +87,7 @@ public: 写出了类似这样的代码: -``` +```CPP if (root->val > root->left->val && root->val < root->right->val) { return true; } else { @@ -95,7 +95,7 @@ if (root->val > root->left->val && root->val < root->right->val) { } ``` -**我们要比较的是 左子树所有节点小于中间节点,右子树所有节点大于中间节点。**所以以上代码的判断逻辑是错误的。 +**我们要比较的是 左子树所有节点小于中间节点,右子树所有节点大于中间节点**。所以以上代码的判断逻辑是错误的。 例如: [10,5,15,null,null,6,20] 这个case: @@ -125,7 +125,7 @@ if (root->val > root->left->val && root->val < root->right->val) { 代码如下: -``` +```CPP long long maxVal = LONG_MIN; // 因为后台测试数据中有int最小值 bool isValidBST(TreeNode* root) ``` @@ -138,7 +138,7 @@ bool isValidBST(TreeNode* root) 代码如下: -``` +```CPP if (root == NULL) return true; ``` @@ -148,7 +148,7 @@ if (root == NULL) return true; 代码如下: -``` +```CPP bool left = isValidBST(root->left); // 左 // 中序遍历,验证遍历的元素是不是从小到大 diff --git a/problems/0104.二叉树的最大深度.md b/problems/0104.二叉树的最大深度.md index 392a3778..2bcc9c5c 100644 --- a/problems/0104.二叉树的最大深度.md +++ b/problems/0104.二叉树的最大深度.md @@ -5,11 +5,6 @@

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

-看完本篇可以一起做了如下两道题目: - -* 104.二叉树的最大深度 -* 559.n叉树的最大深度 - # 104.二叉树的最大深度 [力扣题目链接](https://leetcode.cn/problems/maximum-depth-of-binary-tree/) @@ -27,6 +22,16 @@ 返回它的最大深度 3 。 +# 思路 + +看完本篇可以一起做了如下两道题目: + +* 104.二叉树的最大深度 +* 559.n叉树的最大深度 + +《代码随想录》算法视频公开课:[二叉树的高度和深度有啥区别?究竟用什么遍历顺序?很多录友搞不懂 | 104.二叉树的最大深度](https://www.bilibili.com/video/BV1Gd4y1V75u),相信结合视频在看本篇题解,更有助于大家对本题的理解。 + + ## 递归法 本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。 diff --git a/problems/0110.平衡二叉树.md b/problems/0110.平衡二叉树.md index 5bdd4f1b..a87b64fc 100644 --- a/problems/0110.平衡二叉树.md +++ b/problems/0110.平衡二叉树.md @@ -31,8 +31,10 @@ 返回 false 。 -# 题外话 +**《代码随想录》算法视频公开课:[后序遍历求高度,高度判断是否平衡 | LeetCode:110.平衡二叉树](https://www.bilibili.com/video/BV1Ug411S7my),相信结合视频在看本篇题解,更有助于大家对本题的理解**。 + +## 题外话 咋眼一看这道题目和[104.二叉树的最大深度](https://programmercarl.com/0104.二叉树的最大深度.html)很像,其实有很大区别。 @@ -113,9 +115,9 @@ public: }; ``` -# 本题思路 +## 本题思路 -## 递归 +### 递归 此时大家应该明白了既然要求比较高度,必然是要后序遍历。 @@ -225,7 +227,7 @@ public: }; ``` -## 迭代 +### 迭代 在[104.二叉树的最大深度](https://programmercarl.com/0104.二叉树的最大深度.html)中我们可以使用层序遍历来求深度,但是就不能直接用层序遍历来求高度了,这就体现出求高度和求深度的不同。 @@ -342,7 +344,7 @@ public: 因为对于回溯算法已经是非常复杂的递归了,如果在用迭代的话,就是自己给自己找麻烦,效率也并不一定高。 -# 总结 +## 总结 通过本题可以了解求二叉树深度 和 二叉树高度的差异,求深度适合用前序遍历,而求高度适合用后序遍历。 @@ -351,9 +353,9 @@ public: 但是递归方式是一定要掌握的! -# 其他语言版本 +## 其他语言版本 -## Java +### Java ```Java class Solution { @@ -494,7 +496,7 @@ class Solution { } ``` -## Python +### Python 递归法: ```python @@ -554,7 +556,7 @@ class Solution: ``` -## Go +### Go ```Go func isBalanced(root *TreeNode) bool { if root==nil{ @@ -590,7 +592,7 @@ func abs(a int)int{ } ``` -## JavaScript +### JavaScript 递归法: ```javascript var isBalanced = function(root) { @@ -658,7 +660,7 @@ var isBalanced = function (root) { }; ``` -## TypeScript +### TypeScript ```typescript // 递归法 @@ -676,7 +678,7 @@ function isBalanced(root: TreeNode | null): boolean { }; ``` -## C +### C 递归法: ```c @@ -780,7 +782,7 @@ bool isBalanced(struct TreeNode* root){ } ``` -## Swift: +### Swift: >递归 ```swift diff --git a/problems/0111.二叉树的最小深度.md b/problems/0111.二叉树的最小深度.md index 3c32f620..403027e4 100644 --- a/problems/0111.二叉树的最小深度.md +++ b/problems/0111.二叉树的最小深度.md @@ -27,6 +27,9 @@ # 思路 +《代码随想录》算法视频公开课:[看起来好像做过,一写就错! | LeetCode:111.二叉树的最小深度](https://www.bilibili.com/video/BV1QD4y1B7e2),相信结合视频在看本篇题解,更有助于大家对本题的理解。 + + 看完了这篇[104.二叉树的最大深度](https://programmercarl.com/0104.二叉树的最大深度.html),再来看看如何求最小深度。 直觉上好像和求最大深度差不多,其实还是差不少的。 diff --git a/problems/0112.路径总和.md b/problems/0112.路径总和.md index d4cb5190..d8d4e2f8 100644 --- a/problems/0112.路径总和.md +++ b/problems/0112.路径总和.md @@ -123,9 +123,9 @@ return false; 整体代码如下: ```cpp -class solution { +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; // 遇到叶子节点直接返回 @@ -143,8 +143,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); } }; @@ -155,7 +155,7 @@ public: ```cpp class solution { public: - bool haspathsum(treenode* root, int sum) { + bool hasPathSum(TreeNode* root, int sum) { if (root == null) return false; if (!root->left && !root->right && sum == root->val) { return true; @@ -176,7 +176,7 @@ public: c++就我们用pair结构来存放这个栈里的元素。 -定义为:`pair` pair<节点指针,路径数值> +定义为:`pair` pair<节点指针,路径数值> 这个为栈里的一个元素。 @@ -186,25 +186,25 @@ c++就我们用pair结构来存放这个栈里的元素。 class solution { public: - bool haspathsum(treenode* root, int sum) { + 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; diff --git a/problems/0222.完全二叉树的节点个数.md b/problems/0222.完全二叉树的节点个数.md index 4057053e..dc037f0a 100644 --- a/problems/0222.完全二叉树的节点个数.md +++ b/problems/0222.完全二叉树的节点个数.md @@ -32,6 +32,8 @@ # 思路 +《代码随想录》算法视频公开课:[要理解普通二叉树和完全二叉树的区别! | LeetCode:222.完全二叉树节点的数量](https://www.bilibili.com/video/BV1eW4y1B7pD),相信结合视频在看本篇题解,更有助于大家对本题的理解。 + 本篇给出按照普通二叉树的求法以及利用完全二叉树性质的求法。 ## 普通二叉树 diff --git a/problems/0257.二叉树的所有路径.md b/problems/0257.二叉树的所有路径.md index 0dc9a09b..579c214f 100644 --- a/problems/0257.二叉树的所有路径.md +++ b/problems/0257.二叉树的所有路径.md @@ -20,6 +20,8 @@ # 思路 +**《代码随想录》算法视频公开课:[递归中带着回溯,你感受到了没?| LeetCode:257. 二叉树的所有路径](https://www.bilibili.com/video/BV1ZG411G7Dh),相信结合视频在看本篇题解,更有助于大家对本题的理解**。 + 这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。 在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一一个路径在进入另一个路径。 diff --git a/problems/0404.左叶子之和.md b/problems/0404.左叶子之和.md index 2d522f41..12a2881e 100644 --- a/problems/0404.左叶子之和.md +++ b/problems/0404.左叶子之和.md @@ -17,6 +17,9 @@ # 思路 +**《代码随想录》算法视频公开课:[二叉树的题目中,总有一些规则让你找不到北 | LeetCode:404.左叶子之和](https://www.bilibili.com/video/BV1GY4y1K7z8),相信结合视频在看本篇题解,更有助于大家对本题的理解**。 + + **首先要注意是判断左叶子,不是二叉树左侧节点,所以不要上来想着层序遍历。** 因为题目中其实没有说清楚左叶子究竟是什么节点,那么我来给出左叶子的明确定义:**节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么A节点的左孩子为左叶子节点** diff --git a/problems/0501.二叉搜索树中的众数.md b/problems/0501.二叉搜索树中的众数.md index 671d8061..ad6577dd 100644 --- a/problems/0501.二叉搜索树中的众数.md +++ b/problems/0501.二叉搜索树中的众数.md @@ -72,7 +72,7 @@ void searchBST(TreeNode* cur, unordered_map& map) { // 前序遍历 代码如下: -``` +```CPP bool static cmp (const pair& a, const pair& b) { return a.second > b.second; // 按照频率从大到小排序 } @@ -169,7 +169,7 @@ void searchBST(TreeNode* cur) { 代码如下: -``` +```CPP if (pre == NULL) { // 第一个节点 count = 1; // 频率为1 } else if (pre->val == cur->val) { // 与前一个节点数值相同 @@ -194,7 +194,7 @@ pre = cur; // 更新上一个节点 如果 频率count 等于 maxCount(最大频率),当然要把这个元素加入到结果集中(以下代码为result数组),代码如下: -``` +```CPP if (count == maxCount) { // 如果和最大值相同,放进result中 result.push_back(cur->val); } @@ -206,7 +206,7 @@ if (count == maxCount) { // 如果和最大值相同,放进result中 频率count 大于 maxCount的时候,不仅要更新maxCount,而且要清空结果集(以下代码为result数组),因为结果集之前的元素都失效了。 -``` +```CPP if (count > maxCount) { // 如果计数大于最大值 maxCount = count; // 更新最大频率 result.clear(); // 很关键的一步,不要忘记清空result,之前result里的元素都失效了 diff --git a/problems/0513.找树左下角的值.md b/problems/0513.找树左下角的值.md index 817c22c9..2218a549 100644 --- a/problems/0513.找树左下角的值.md +++ b/problems/0513.找树左下角的值.md @@ -41,7 +41,7 @@ 所以要找深度最大的叶子节点。 -那么如果找最左边的呢?可以使用前序遍历,这样才先优先左边搜索,然后记录深度最大的叶子节点,此时就是树的最后一行最左边的值。 +那么如果找最左边的呢?可以使用前序遍历(当然中序,后序都可以,因为本题没有 中间节点的处理逻辑,只要左优先就行),保证优先左边搜索,然后记录深度最大的叶子节点,此时就是树的最后一行最左边的值。 递归三部曲: @@ -49,25 +49,16 @@ 参数必须有要遍历的树的根节点,还有就是一个int型的变量用来记录最长深度。 这里就不需要返回值了,所以递归函数的返回类型为void。 -本题还需要类里的两个全局变量,maxLen用来记录最大深度,maxleftValue记录最大深度最左节点的数值。 +本题还需要类里的两个全局变量,maxLen用来记录最大深度,result记录最大深度最左节点的数值。 代码如下: ```CPP -int maxLen = INT_MIN; // 全局变量 记录最大深度 -int maxleftValue; // 全局变量 最大深度最左节点的数值 -void traversal(TreeNode* root, int leftLen) +int maxDepth = INT_MIN; // 全局变量 记录最大深度 +int result; // 全局变量 最大深度最左节点的数值 +void traversal(TreeNode* root, int depth) ``` -有的同学可能疑惑,为啥不能递归函数的返回值返回最长深度呢? - -其实很多同学都对递归函数什么时候要有返回值,什么时候不能有返回值很迷茫。 - -**如果需要遍历整棵树,递归函数就不能有返回值。如果需要遍历某一条固定路线,递归函数就一定要有返回值!** - -初学者可能对这个结论不太理解,别急,后面我会安排一道题目专门讲递归函数的返回值问题。这里大家暂时先了解一下。 - -本题我们是要遍历整个树找到最深的叶子节点,需要遍历整棵树,所以递归函数没有返回值。 2. 确定终止条件 @@ -77,9 +68,9 @@ void traversal(TreeNode* root, int leftLen) ```CPP if (root->left == NULL && root->right == NULL) { - if (leftLen > maxLen) { - maxLen = leftLen; // 更新最大深度 - maxleftValue = root->val; // 最大深度最左面的数值 + if (depth > maxDepth) { + maxDepth = depth; // 更新最大深度 + result = root->val; // 最大深度最左面的数值 } return; } @@ -92,14 +83,14 @@ if (root->left == NULL && root->right == NULL) { ```CPP // 中 if (root->left) { // 左 - leftLen++; // 深度加一 - traversal(root->left, leftLen); - leftLen--; // 回溯,深度减一 + depth++; // 深度加一 + traversal(root->left, depth); + depth--; // 回溯,深度减一 } if (root->right) { // 右 - leftLen++; // 深度加一 - traversal(root->right, leftLen); - leftLen--; // 回溯,深度减一 + depth++; // 深度加一 + traversal(root->right, depth); + depth--; // 回溯,深度减一 } return; ``` @@ -109,31 +100,31 @@ return; ```CPP class Solution { public: - int maxLen = INT_MIN; - int maxleftValue; - void traversal(TreeNode* root, int leftLen) { + int maxDepth = INT_MIN; + int result; + void traversal(TreeNode* root, int depth) { if (root->left == NULL && root->right == NULL) { - if (leftLen > maxLen) { - maxLen = leftLen; - maxleftValue = root->val; + if (depth > maxDepth) { + maxDepth = depth; + result = root->val; } return; } if (root->left) { - leftLen++; - traversal(root->left, leftLen); - leftLen--; // 回溯 + depth++; + traversal(root->left, depth); + depth--; // 回溯 } if (root->right) { - leftLen++; - traversal(root->right, leftLen); - leftLen--; // 回溯 + depth++; + traversal(root->right, depth); + depth--; // 回溯 } return; } int findBottomLeftValue(TreeNode* root) { traversal(root, 0); - return maxleftValue; + return result; } }; ``` @@ -143,27 +134,27 @@ public: ```CPP class Solution { public: - int maxLen = INT_MIN; - int maxleftValue; - void traversal(TreeNode* root, int leftLen) { + int maxDepth = INT_MIN; + int result; + void traversal(TreeNode* root, int depth) { if (root->left == NULL && root->right == NULL) { - if (leftLen > maxLen) { - maxLen = leftLen; - maxleftValue = root->val; + if (depth > maxDepth) { + maxDepth = depth; + result = root->val; } return; } if (root->left) { - traversal(root->left, leftLen + 1); // 隐藏着回溯 + traversal(root->left, depth + 1); // 隐藏着回溯 } if (root->right) { - traversal(root->right, leftLen + 1); // 隐藏着回溯 + traversal(root->right, depth + 1); // 隐藏着回溯 } return; } int findBottomLeftValue(TreeNode* root) { traversal(root, 0); - return maxleftValue; + return result; } }; ``` @@ -555,14 +546,14 @@ object Solution { var maxLeftValue = 0 var maxLen = Int.MinValue // 递归方法 - def traversal(node: TreeNode, leftLen: Int): Unit = { + def traversal(node: TreeNode, depth: Int): Unit = { // 如果左右都为空并且当前深度大于最大深度,记录最左节点的值 - if (node.left == null && node.right == null && leftLen > maxLen) { - maxLen = leftLen + if (node.left == null && node.right == null && depth > maxLen) { + maxLen = depth maxLeftValue = node.value } - if (node.left != null) traversal(node.left, leftLen + 1) - if (node.right != null) traversal(node.right, leftLen + 1) + if (node.left != null) traversal(node.left, depth + 1) + if (node.right != null) traversal(node.right, depth + 1) } traversal(root, 0) // 调用方法 maxLeftValue // return关键字可以省略 diff --git a/problems/0530.二叉搜索树的最小绝对差.md b/problems/0530.二叉搜索树的最小绝对差.md index 809f500b..9611df99 100644 --- a/problems/0530.二叉搜索树的最小绝对差.md +++ b/problems/0530.二叉搜索树的最小绝对差.md @@ -77,7 +77,7 @@ public: class Solution { private: int result = INT_MAX; -TreeNode* pre; +TreeNode* pre = NULL; void traversal(TreeNode* cur) { if (cur == NULL) return; traversal(cur->left); // 左 diff --git a/problems/0700.二叉搜索树中的搜索.md b/problems/0700.二叉搜索树中的搜索.md index a8dcc69f..22f009cb 100644 --- a/problems/0700.二叉搜索树中的搜索.md +++ b/problems/0700.二叉搜索树中的搜索.md @@ -41,7 +41,7 @@ 代码如下: -``` +```CPP TreeNode* searchBST(TreeNode* root, int val) ``` @@ -49,7 +49,7 @@ TreeNode* searchBST(TreeNode* root, int val) 如果root为空,或者找到这个数值了,就返回root节点。 -``` +```CPP if (root == NULL || root->val == val) return root; ``` @@ -63,20 +63,36 @@ if (root == NULL || root->val == val) return root; 代码如下: -``` -if (root->val > val) return searchBST(root->left, val); // 注意这里加了return -if (root->val < val) return searchBST(root->right, val); -return NULL; +```CPP +TreeNode* result = NULL; +if (root->val > val) result = searchBST(root->left, val); +if (root->val < val) result = searchBST(root->right, val); +return result; ``` -这里可能会疑惑,在递归遍历的时候,什么时候直接return 递归函数的返回值,什么时候不用加这个 return呢。 +很多录友写递归函数的时候 习惯直接写 `searchBST(root->left, val)`,却忘了 递归函数还有返回值。 -我们在[二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?](https://programmercarl.com/0112.路径总和.html)中讲了,如果要搜索一条边,递归函数就要加返回值,这里也是一样的道理。 +递归函数的返回值是什么? 是 左子树如果搜索到了val,要将该节点返回。 如果不用一个变量将其接住,那么返回值不就没了。 -**因为搜索到目标节点了,就要立即return了,这样才是找到节点就返回(搜索某一条边),如果不加return,就是遍历整棵树了。** +所以要 `result = searchBST(root->left, val)`。 整体代码如下: +```CPP +class Solution { +public: + TreeNode* searchBST(TreeNode* root, int val) { + if (root == NULL || root->val == val) return root; + TreeNode* result = NULL; + if (root->val > val) result = searchBST(root->left, val); + if (root->val < val) result = searchBST(root->right, val); + return result; + } +}; +``` + +或者我们也可以这么写 + ```CPP class Solution { public: @@ -89,6 +105,7 @@ public: }; ``` + ## 迭代法 一提到二叉树遍历的迭代法,可能立刻想起使用栈来模拟深度遍历,使用队列来模拟广度遍历。