This commit is contained in:
programmercarl
2022-09-16 09:40:24 +08:00
parent 697b8ac8e8
commit a64f7a1734
12 changed files with 124 additions and 99 deletions

View File

@ -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); // 左
// 中序遍历,验证遍历的元素是不是从小到大

View File

@ -5,11 +5,6 @@
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
看完本篇可以一起做了如下两道题目:
* 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),相信结合视频在看本篇题解,更有助于大家对本题的理解。
## 递归法
本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。

View File

@ -31,8 +31,10 @@
返回 false 。
# 题外话
**《代码随想录》算法视频公开课:[后序遍历求高度,高度判断是否平衡 | LeetCode110.平衡二叉树](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

View File

@ -27,6 +27,9 @@
# 思路
《代码随想录》算法视频公开课:[看起来好像做过,一写就错! | LeetCode111.二叉树的最小深度](https://www.bilibili.com/video/BV1QD4y1B7e2),相信结合视频在看本篇题解,更有助于大家对本题的理解。
看完了这篇[104.二叉树的最大深度](https://programmercarl.com/0104.二叉树的最大深度.html),再来看看如何求最小深度。
直觉上好像和求最大深度差不多,其实还是差不少的。

View File

@ -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<treenode*, int>` pair<节点指针路径数值>
定义为:`pair<TreeNode*, int>` 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<pair<treenode*, int>> st;
st.push(pair<treenode*, int>(root, root->val));
stack<pair<TreeNode*, int>> st;
st.push(pair<TreeNode*, int>(root, root->val));
while (!st.empty()) {
pair<treenode*, int> node = st.top();
pair<TreeNode*, int> 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<treenode*, int>(node.first->right, node.second + node.first->right->val));
st.push(pair<TreeNode*, int>(node.first->right, node.second + node.first->right->val));
}
// 左节点,压进去一个节点的时候,将该节点的路径数值也记录下来
if (node.first->left) {
st.push(pair<treenode*, int>(node.first->left, node.second + node.first->left->val));
st.push(pair<TreeNode*, int>(node.first->left, node.second + node.first->left->val));
}
}
return false;

View File

@ -32,6 +32,8 @@
# 思路
《代码随想录》算法视频公开课:[要理解普通二叉树和完全二叉树的区别! | LeetCode222.完全二叉树节点的数量](https://www.bilibili.com/video/BV1eW4y1B7pD),相信结合视频在看本篇题解,更有助于大家对本题的理解。
本篇给出按照普通二叉树的求法以及利用完全二叉树性质的求法。
## 普通二叉树

View File

@ -20,6 +20,8 @@
# 思路
**《代码随想录》算法视频公开课:[递归中带着回溯,你感受到了没?| LeetCode257. 二叉树的所有路径](https://www.bilibili.com/video/BV1ZG411G7Dh),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。
在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一一个路径在进入另一个路径。

View File

@ -17,6 +17,9 @@
# 思路
**《代码随想录》算法视频公开课:[二叉树的题目中,总有一些规则让你找不到北 | LeetCode404.左叶子之和](https://www.bilibili.com/video/BV1GY4y1K7z8),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
**首先要注意是判断左叶子,不是二叉树左侧节点,所以不要上来想着层序遍历。**
因为题目中其实没有说清楚左叶子究竟是什么节点,那么我来给出左叶子的明确定义:**节点A的左孩子不为空且左孩子的左右孩子都为空说明是叶子节点那么A节点的左孩子为左叶子节点**

View File

@ -72,7 +72,7 @@ void searchBST(TreeNode* cur, unordered_map<int, int>& map) { // 前序遍历
代码如下:
```
```CPP
bool static cmp (const pair<int, int>& a, const pair<int, int>& 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里的元素都失效了

View File

@ -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关键字可以省略

View File

@ -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); // 左

View File

@ -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:
};
```
## 迭代法
一提到二叉树遍历的迭代法,可能立刻想起使用栈来模拟深度遍历,使用队列来模拟广度遍历。