This commit is contained in:
youngyangyang04
2021-08-06 15:34:33 +08:00
parent 5d7e0288a1
commit 029cf012ee
18 changed files with 674 additions and 575 deletions

View File

@ -409,6 +409,7 @@
2. [单调栈下一个更大元素I](./problems/0496.下一个更大元素I.md)
3. [单调栈下一个更大元素II](./problems/0503.下一个更大元素II.md)
4. [单调栈:接雨水](./problems/0042.接雨水.md)
5. [单调栈:柱状图中最大的矩形](./problems/0084.柱状图中最大的矩形.md)
(持续更新中....

View File

@ -91,12 +91,12 @@ 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增加一个元素使其word1[i - 1]与word2[j - 1]相同那么就是以下标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添加一个元素使其word1[i - 1]与word2[j - 1]相同那么就是以下标i-1为结尾的word1 与 j-2为结尾的word2的最近编辑距离 加上一个增加元素的操作。
`dp[i][j] = dp[i][j - 1] + 1;`

View File

@ -0,0 +1,193 @@
# 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)
我们先来看一下双指针的解法:
## 双指针解法
```C++
class Solution {
public:
int largestRectangleArea(vector<int>& 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)中已经介绍了。
```C++
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
vector<int> minLeftIndex(heights.size());
vector<int> 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++代码如下:
```C++
// 版本一
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
stack<int> 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;
}
};
```
代码精简之后:
```C++
// 版本二
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
stack<int> 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;
}
};
```
这里我依然建议大家按部就班把版本一写出来,把情况一二三分析清楚,然后在精简代码到版本二。 直接看版本二容易忽略细节!

View File

@ -251,6 +251,8 @@ public:
# 相关题目推荐
这两道题目基本和本题是一样的只要稍加修改就可以AC。
* 100.相同的树
* 572.另一个树的子树

View File

@ -6,11 +6,8 @@
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 二叉树的层序遍历
看完这篇文章虽然不能打十个,但是可以迅速打八个!而且够快!
学会二叉树的层序遍历可以一口气撸完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++代码:
```
```C++
class Solution {
public:
vector<vector<int>> 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/
@ -404,7 +408,7 @@ var levelOrderBottom = function(root) {
```
## 199.二叉树的右视图
# 199.二叉树的右视图
题目链接https://leetcode-cn.com/problems/binary-tree-right-side-view/
@ -581,7 +585,7 @@ var rightSideView = function(root) {
};
```
## 637.二叉树的层平均值
# 637.二叉树的层平均值
题目链接https://leetcode-cn.com/problems/average-of-levels-in-binary-tree/
@ -765,7 +769,7 @@ var averageOfLevels = function(root) {
};
```
## 429.N叉树的层序遍历
# 429.N叉树的层序遍历
题目链接https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/
@ -985,7 +989,7 @@ var levelOrder = function(root) {
};
```
## 515.在每个树行中找最大值
# 515.在每个树行中找最大值
题目链接https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/
@ -1115,7 +1119,7 @@ var largestValues = function(root) {
};
```
## 116.填充每个节点的下一个右侧节点指针
# 116.填充每个节点的下一个右侧节点指针
题目链接https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/
@ -1254,7 +1258,7 @@ func connect(root *Node) *Node {
}
```
## 117.填充每个节点的下一个右侧节点指针II
# 117.填充每个节点的下一个右侧节点指针II
题目地址https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii/
@ -1295,6 +1299,7 @@ public:
}
};
```
python代码
```python
@ -1378,12 +1383,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++代码如下:
```C++
class Solution {
public:
int maxDepth(TreeNode* root) {
if (root == NULL) return 0;
int depth = 0;
queue<TreeNode*> 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.二叉树的最大深度 ,本题还也可以使用层序遍历的方式来解决,思路是一样的。
**需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点**
代码如下:(详细注释)
```C++
class Solution {
public:
int minDepth(TreeNode* root) {
if (root == NULL) return 0;
int depth = 0;
queue<TreeNode*> 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,287 +1505,13 @@ 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)

View File

@ -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<TreeNode*> que;
queue<treenode*> 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<Node*> que;
if (root != NULL) que.push(root);
int maxdepth(node* root) {
queue<node*> 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<TreeNode> deque = new LinkedList<>();
deque<treenode> 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);

View File

@ -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,11 +51,11 @@
因为求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中)
有的同学一定疑惑,为什么[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++
class Solution {
@ -114,9 +115,9 @@ public:
};
```
## 本题思路
# 本题思路
### 递归
## 递归
此时大家应该明白了既然要求比较高度,必然是要后序遍历。
@ -160,7 +161,7 @@ if (node == NULL) {
代码如下:
```
```C++
int leftDepth = depth(node->left); // 左
if (leftDepth == -1) return -1;
int rightDepth = depth(node->right); // 右
@ -178,7 +179,7 @@ return result;
代码精简之后如下:
```
```C++
int leftDepth = getDepth(node->left);
if (leftDepth == -1) return -1;
int rightDepth = getDepth(node->right);
@ -225,9 +226,9 @@ public:
};
```
### 迭代
## 迭代
在[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中我们可以使用层序遍历来求深度,但是就不能直接用层序遍历来求高度了,这就体现出求高度和求深度的不同。
在[104.二叉树的最大深度](https://mp.weixin.qq.com/s/jRaRcRerhEHepQbt-aKstw)中我们可以使用层序遍历来求深度,但是就不能直接用层序遍历来求高度了,这就体现出求高度和求深度的不同。
本题的迭代方式可以先定义一个函数,专门用来求高度。
@ -266,7 +267,7 @@ int getDepth(TreeNode* cur) {
然后再用栈来模拟前序遍历,遍历每一个节点的时候,再去判断左右孩子的高度是否符合,代码如下:
```
```C++
bool isBalanced(TreeNode* root) {
stack<TreeNode*> st;
if (root == NULL) return true;
@ -286,7 +287,7 @@ bool isBalanced(TreeNode* root) {
整体代码如下:
```
```C++
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

View File

@ -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),再来看看如何求最小深度。
直觉上好像和求最大深度差不多,其实还是差不少的。
@ -154,9 +154,9 @@ 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)
**需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点**
@ -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
递归法:

View File

@ -7,24 +7,23 @@
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
## 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类型。
@ -107,15 +107,15 @@ 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统计节点数量就可以了
@ -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),这篇详细介绍了各种二叉树的特性。
完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。
@ -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

View File

@ -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)
## 思路
# 思路
这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。
@ -215,8 +215,52 @@ public:
那么在如上代码中,**貌似没有看到回溯的逻辑,其实不然,回溯就隐藏在`traversal(cur->left, path + "->", result);`中的 `path + "->"`。** 每次函数调用完path依然是没有加上"->" 的,这就是回溯了。
**如果这里还不理解的话,可以看这篇[二叉树:以为使用了递归,其实还隐藏着回溯](https://mp.weixin.qq.com/s/ivLkHzWdhjQQD1rQWe6zWA),我这这篇中详细的解释了递归中如何隐藏着回溯。 **
为了把这份精简代码的回溯过程展现出来,大家可以试一试把:
```C++
if (cur->left) traversal(cur->left, path + "->", result); // 左 回溯就隐藏在这里
```
改成如下代码:
```C++
path += "->";
traversal(cur->left, path, result); // 左
```
即:
```C++
if (cur->left) {
path += "->";
traversal(cur->left, path, result); // 左
}
if (cur->right) {
path += "->";
traversal(cur->right, path, result); // 右
}
```
此时就没有回溯了,这个代码就是通过不了的了。
如果想把回溯加上,就要 在上面代码的基础上加上回溯就可以AC了。
```C++
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,7 +269,8 @@ 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)。
这里除了模拟递归需要一个栈,同时还需要一个栈来存放对应的遍历路径。
@ -262,7 +307,7 @@ public:
```
当然使用java的同学可以直接定义一个成员变量为object的栈`Stack<Object> stack = new Stack<>();`,这样就不用定义两个栈了,都放到一个栈里就可以了。
## 总结
# 总结
**本文我们开始初步涉及到了回溯,很多同学过了这道题目,可能都不知道自己其实使用了回溯,回溯和递归都是相伴相生的。**
@ -278,7 +323,7 @@ public:
## 其他语言版本
# 其他语言版本
Java
@ -321,62 +366,10 @@ class Solution {
}
}
}
//解法二(常规前序遍历,不用回溯),更容易理解
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> res = new ArrayList<>();
helper(root, new StringBuilder(), res);
return res;
}
public void helper(TreeNode root, StringBuilder sb, List<String> 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<String> binaryTreePaths(TreeNode root) {
List<String> res = new ArrayList<>();
helper(root, "", res);
return res;
}
public void helper(TreeNode root, String path, List<String> 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) {
//递归遍历+递归三部曲

View File

@ -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的初始化

View File

@ -1,9 +1,41 @@
## 题目链接
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
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
# 思路
判断是山峰,主要就是要严格的保存左边到中间,和右边到中间是递增的。
@ -38,7 +70,12 @@ public:
}
};
```
Java 版本如下:
如果想系统学一学双指针的话, 可以看一下这篇[双指针法:总结篇!](https://mp.weixin.qq.com/s/_p7grwjISfMh0U65uOyCjA)
# 其他语言版本
## Java
```java
class Solution {
@ -66,5 +103,26 @@ class Solution {
}
```
如果想系统学一学双指针的话, 可以看一下这篇[双指针法:总结篇!](https://mp.weixin.qq.com/s/_p7grwjISfMh0U65uOyCjA)
## 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)
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>

View File

@ -120,13 +120,13 @@ public:
为了把这份精简代码的回溯过程展现出来,大家可以试一试把:
```
```C++
if (cur->left) traversal(cur->left, path + "->", result); // 左 回溯就隐藏在这里
```
改成如下代码:
```
```C++
path += "->";
traversal(cur->left, path, result); // 左
```
@ -149,7 +149,7 @@ if (cur->right) {
如果想把回溯加上,就要 在上面代码的基础上加上回溯就可以AC了。
```
```C++
if (cur->left) {
path += "->";
traversal(cur->left, path, result); // 左

View File

@ -223,12 +223,12 @@ public:
文中我明确的说了:**回溯就隐藏在traversal(cur->left, path + "->", result);中的 path + "->"。 每次函数调用完path依然是没有加上"->" 的,这就是回溯了。**
如果还不理解的话,可以把
```
```C++
traversal(cur->left, path + "->", result);
```
改成
```
```C++
string tmp = path + "->";
traversal(cur->left, tmp, result);
```

View File

@ -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 = cc就是12。

View File

@ -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]);

View File

@ -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指定是取最大的,毕竟是求最大价值,
所以递归公式为:

View File

@ -92,7 +92,7 @@ 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])。