This commit is contained in:
youngyangyang04
2020-10-03 07:48:03 +08:00
parent b872cf6da8
commit a86f06e3f3
10 changed files with 546 additions and 69 deletions

View File

@ -284,6 +284,7 @@
|[0486.预测赢家](https://github.com/youngyangyang04/leetcode/blob/master/problems/0486.预测赢家.md) |动态规划 |中等| **递归** **记忆递归** **动态规划**|
|[0491.递增子序列](https://github.com/youngyangyang04/leetcode/blob/master/problems/0491.递增子序列.md) |深度优先搜索 |中等|**深度优先搜索/回溯算法**|
|[0501.二叉搜索树中的众数](https://github.com/youngyangyang04/leetcode/blob/master/problems/0501.二叉搜索树中的众数.md) |二叉树 |简单|**递归/中序遍历**|
|[0513.找树左下角的值](https://github.com/youngyangyang04/leetcode/blob/master/problems/0513.找树左下角的值.md) |二叉树 |中等|**递归** **迭代**|
|[0515.在每个树行中找最大值](https://github.com/youngyangyang04/leetcode/blob/master/problems/0515.在每个树行中找最大值.md) |二叉树 |简单|**广度优先搜索/队列**|
|[0538.把二叉搜索树转换为累加树](https://github.com/youngyangyang04/leetcode/blob/master/problems/0538.把二叉搜索树转换为累加树.md) |二叉树 |简单|**递归** **迭代**|
|[0541.反转字符串II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0541.反转字符串II.md) |字符串 |简单| **模拟**|

BIN
pics/100.相同的树.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -1,9 +1,115 @@
## 题目地址
https://leetcode-cn.com/problems/same-tree/
## 思路
(没写完)
这道题目和101 基本是一样的
# 100. 相同的树
给定两个二叉树,编写一个函数来检验它们是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
<img src='../pics/100.相同的树.png' width=600> </img></div>
# 思路
在[二叉树:我对称么?](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg)中,我们讲到对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了**其实我们要比较的是两个树(这两个树是根节点的左右子树)**,所以在递归遍历的过程中,也是要同时遍历两棵树。
理解这一本质之后,就会发现,求二叉树是否对称,和求二叉树是否相同几乎是同一道题目。
**如果没有读过[二叉树:我对称么?](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg)这一篇,请认真读完再做这道题,就会有感觉了。**
递归三部曲中:
1. 确定递归函数的参数和返回值
我们要比较的是两个树是否是相互相同的,参数也就是两个树的根节点。
返回值自然是bool类型。
代码如下:
```
bool compare(TreeNode* tree1, TreeNode* tree2)
```
分析过程同[二叉树:我对称么?](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg)。
2. 确定终止条件
**要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。**
节点为空的情况有:
* tree1为空tree2不为空不对称return false
* tree1不为空tree2为空不对称 return false
* tree1tree2都为空对称返回true
此时已经排除掉了节点为空的情况那么剩下的就是tree1和tree2不为空的时候
* tree1、tree2都不为空比较节点数值不相同就return false
此时tree1、tree2节点不为空且数值也不相同的情况我们也处理了。
代码如下:
```
if (tree1 == NULL && tree2 != NULL) return false;
else if (tree1 != NULL && tree2 == NULL) return false;
else if (tree1 == NULL && tree2 == NULL) return true;
else if (tree1->val != tree2->val) return false; // 注意这里我没有使用else
```
分析过程同[二叉树:我对称么?](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg)
3. 确定单层递归的逻辑
* 比较二叉树是否相同 传入的是tree1的左孩子tree2的右孩子。
* 如果左右都相同就返回true 有一侧不相同就返回false 。
代码如下:
```
bool outside = compare(tree1->left, tree2->right); // 左子树:左、 右子树:左
bool inside = compare(tree1->right, tree2->left); // 左子树:右、 右子树:右
bool isSame = outside && inside; // 左子树:中、 右子树:中(逻辑处理)
return isSame;
```
-------------------------------------------------------------- 写到这
最后递归的C++整体代码如下:
```
class Solution {
public:
bool compare(TreeNode* left, TreeNode* right) {
// 首先排除空节点的情况
if (left == NULL && right != NULL) return false;
else if (left != NULL && right == NULL) return false;
else if (left == NULL && right == NULL) return true;
// 排除了空节点,再排除数值不相同的情况
else if (left->val != right->val) return false;
// 此时就是:左右节点都不为空,且数值相同的情况
// 此时才做递归,做下一层的判断
bool outside = compare(left->left, right->right); // 左子树:左、 右子树:右
bool inside = compare(left->right, right->left); // 左子树:右、 右子树:左
bool isSame = outside && inside; // 左子树:中、 右子树:中 (逻辑处理)
return isSame;
}
bool isSymmetric(TreeNode* root) {
if (root == NULL) return true;
return compare(root->left, root->right);
}
};
```
**我给出的代码并不简洁,但是把每一步判断的逻辑都清楚的描绘出来了。**
如果上来就看网上各种简洁的代码,看起来真的很简单,但是很多逻辑都掩盖掉了,而题解可能也没有把掩盖掉的逻辑说清楚。
**盲目的照着抄,结果就是:发现这是一道“简单题”,稀里糊涂的就过了,但是真正的每一步判断逻辑未必想到清楚。**
当然我可以把如上代码整理如下:
这道题目本质上和[二叉树:我对称么?](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg)是一样,因为
## 递归
@ -42,7 +148,6 @@ public:
if (!leftNode && !rightNode) { //
continue;
}
//
if ((!leftNode || !rightNode || (leftNode->val != rightNode->val))) {
return false;

View File

@ -1,6 +1,8 @@
## 题目地址
https://leetcode-cn.com/problems/balanced-binary-tree/
> 求高度还是求深度,你搞懂了不?
# 110.平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
@ -17,30 +19,117 @@ https://leetcode-cn.com/problems/balanced-binary-tree/
示例 2:
给定二叉树 [1,2,2,3,3,null,null,4,4
给定二叉树 [1,2,2,3,3,null,null,4,4]
<img src='../pics/110.平衡二叉树1.png' width=600> </img></div>
返回 false 。
## 思路
# 题外话
分析一下这道题是不是和求深度很像,在[二叉树:看看这些树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中是求左右子树的最大深度本题呢是要求左右子树高度差绝对值不超过1
咋眼一看这道题目和[二叉树:看看这些树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)很像,其实有很大区别
这里强调一波概念:
* 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。
* 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。
但leetcode中强调的深度和高度很明显是按照节点来计算的如图
<img src='../pics/110.平衡二叉树2.png' width=600> </img></div>
关于根节点的深度究竟是1 还是 0不同的地方有不一样的标准leetcode的题目中都是以节点为一度即根节点深度是1。但维基百科上定义用边为一度即根节点的深度是0我们暂时以leetcode为准毕竟要在这上面刷题
因为求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中)
有的同学一定疑惑,为什么[二叉树:看看这些树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中求的是二叉树的最大深度,也用的是后序遍历。
**那是因为代码的逻辑其实是求的根节点的高度,而根节点的高度就是这颗树的最大深度,所以才可以使用后序遍历。**
在[二叉树:看看这些树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中,如果真正求取二叉树的最大深度,代码应该写成如下:(前序遍历)
```
class Solution {
public:
int result;
void getDepth(TreeNode* node, int depth) {
result = depth > result ? depth : result; // 中
if (node->left == NULL && node->right == NULL) return ;
if (node->left) { // 左
depth++; // 深度+1
getDepth(node->left, depth);
depth--; // 回溯,深度-1
}
if (node->right) { // 右
depth++; // 深度+1
getDepth(node->right, depth);
depth--; // 回溯,深度-1
}
return ;
}
int maxDepth(TreeNode* root) {
result = 0;
if (root == 0) return result;
getDepth(root, 1);
return result;
}
};
```
**可以看出使用了前序(中左右)的遍历顺序,这才是真正求深度的逻辑!**
注意以上代码是为了把细节体现出来,简化一下代码如下:
```
class Solution {
public:
int result;
void getDepth(TreeNode* node, int depth) {
result = depth > result ? depth : result; // 中
if (node->left == NULL && node->right == NULL) return ;
if (node->left) { // 左
getDepth(node->left, depth + 1);
}
if (node->right) { // 右
getDepth(node->right, depth + 1);
}
return ;
}
int maxDepth(TreeNode* root) {
result = 0;
if (root == 0) return result;
getDepth(root, 1);
return result;
}
};
```
# 本题思路
## 递归
此时大家应该明白了既然要求比较高度,必然是要后序遍历。
递归三步曲分析:
1. 明确递归函数的参数和返回值
参数的话为传入的节点指针,就没有其他参数需要传递了,**返回值要注意,我们的返回值是要求传入节点为根节点树的深度**否则如何标记左右子树是否差值大于1呢
参数的话为传入的节点指针,就没有其他参数需要传递了,返回值要返回传入节点为根节点树的深度
这里还要注意一点,如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,返回高度还有意义么? 此时可以返回-1 来标记已经不符合平衡树的规则了
那么如何标记左右子树是否差值大于1呢
如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。
所以如果已经不是二叉平衡树了,可以返回-1 来标记已经不符合平衡树的规则了。
代码如下:
```
int depth(TreeNode* node)
// -1 表示已经不是平衡二叉树了,否则返回值是以该节点为根节点树的高度
int getDepth(TreeNode* node)
```
2. 明确终止条件
@ -64,52 +153,193 @@ if (node == NULL) {
代码如下:
```
int leftDepth = depth(node->left);
if (leftDepth == -1) return -1;
int rightDepth = depth(node->right);
int leftDepth = depth(node->left); // 左
if (leftDepth == -1) return -1;
int rightDepth = depth(node->right); // 右
if (rightDepth == -1) return -1;
int result;
if (abs(leftDepth - rightDepth) > 1) { // 中
result = -1;
} else {
result = 1 + max(leftDepth, rightDepth); // 以当前节点为根节点的最大高度
}
return result;
```
代码精简之后如下:
```
int leftDepth = getDepth(node->left);
if (leftDepth == -1) return -1;
int rightDepth = getDepth(node->right);
if (rightDepth == -1) return -1;
return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth);
```
此时递归的函数就已经写出来了,这个递归的函数传入节点指针,返回以该节点为根节点的二叉树的高度,如果不是二叉平衡树则返回-1。
此时递归的函数就已经写出来了,这个递归的函数传入节点指针,返回以该节点为根节点的二叉树的高度,如果不是二叉平衡树则返回-1。
代码如下:
getDepth整体代码如下:
```
int depth(TreeNode* node) {
int getDepth(TreeNode* node) {
if (node == NULL) {
return 0;
}
int leftDepth = depth(node->left);
int leftDepth = getDepth(node->left);
if (leftDepth == -1) return -1;
int rightDepth = depth(node->right);
int rightDepth = getDepth(node->right);
if (rightDepth == -1) return -1;
return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth);
}
```
整体代码如下:
## C++代码
最后本题整体递归代码如下:
```
class Solution {
public:
// 返回以该节点为根节点的二叉树的高度,如果不是二叉搜索树了则返回-1
int depth(TreeNode* node) {
int getDepth(TreeNode* node) {
if (node == NULL) {
return 0;
}
int leftDepth = depth(node->left);
int leftDepth = getDepth(node->left);
if (leftDepth == -1) return -1; // 说明左子树已经不是二叉平衡树
int rightDepth = depth(node->right);
int rightDepth = getDepth(node->right);
if (rightDepth == -1) return -1; // 说明右子树已经不是二叉平衡树
return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth);
}
bool isBalanced(TreeNode* root) {
return depth(root) == -1 ? false : true;
return getDepth(root) == -1 ? false : true;
}
};
```
## 迭代
在[二叉树:看看这些树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中我们可以使用层序遍历来求深度,但是就不能直接用层序遍历来求高度了,这就体现出求高度和求深度的不同。
本题的迭代方式可以先定义一个函数,专门用来求高度。
这个函数通过栈模拟的后序遍历找每一个节点的高度(其实是通过求传入节点为根节点的最大深度来求的高度)
代码如下:
```
// cur节点的最大深度就是cur的高度
int getDepth(TreeNode* cur) {
stack<TreeNode*> st;
if (cur != NULL) st.push(cur);
int depth = 0; // 记录深度
int result = 0;
while (!st.empty()) {
TreeNode* node = st.top();
if (node != NULL) {
st.pop();
st.push(node); // 中
st.push(NULL);
depth++;
if (node->right) st.push(node->right); // 右
if (node->left) st.push(node->left); // 左
} else {
st.pop();
node = st.top();
st.pop();
depth--;
}
result = result > depth ? result : depth;
}
return result;
}
```
然后再用栈来模拟前序遍历,遍历每一个节点的时候,再去判断左右孩子的高度是否符合,代码如下:
```
bool isBalanced(TreeNode* root) {
stack<TreeNode*> st;
if (root == NULL) return true;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top(); // 中
st.pop();
if (abs(getDepth(node->left) - getDepth(node->right)) > 1) { // 判断左右孩子高度是否符合
return false;
}
if (node->right) st.push(node->right); // 右(空节点不入栈)
if (node->left) st.push(node->left); // 左(空节点不入栈)
}
return true;
}
```
整体代码如下:
```
class Solution {
private:
int getDepth(TreeNode* cur) {
stack<TreeNode*> st;
if (cur != NULL) st.push(cur);
int depth = 0; // 记录深度
int result = 0;
while (!st.empty()) {
TreeNode* node = st.top();
if (node != NULL) {
st.pop();
st.push(node); // 中
st.push(NULL);
depth++;
if (node->right) st.push(node->right); // 右
if (node->left) st.push(node->left); // 左
} else {
st.pop();
node = st.top();
st.pop();
depth--;
}
result = result > depth ? result : depth;
}
return result;
}
public:
bool isBalanced(TreeNode* root) {
stack<TreeNode*> st;
if (root == NULL) return true;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top(); // 中
st.pop();
if (abs(getDepth(node->left) - getDepth(node->right)) > 1) {
return false;
}
if (node->right) st.push(node->right); // 右(空节点不入栈)
if (node->left) st.push(node->left); // 左(空节点不入栈)
}
return true;
}
};
```
当然此题用迭代法,其实效率很低,因为没有很好的模拟回溯的过程,所以迭代法有很多重复的计算。
虽然理论上所有的递归都可以用迭代来实现,但是有的场景难度可能比较大。
**例如:都知道回溯法其实就是递归,但是很少人用迭代的方式去实现回溯算法!**
因为对于回溯算法已经是非常复杂的递归了,如果在用迭代的话,就是自己给自己找麻烦,效率也并不一定高。
# 总结
通过本题可以了解求二叉树深度 和 二叉树高度的差异,求深度适合用前序遍历,而求高度适合用后序遍历。
本题迭代法其实有点复杂,大家可以有一个思路,也不一定说非要写出来。
但是递归方式是一定要掌握的!
> 更多算法干货文章持续更新可以微信搜索「代码随想录」第一时间围观关注后回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等就可以获得我多年整理的学习资料。

View File

@ -1,42 +1,34 @@
## 题目地址
https://leetcode-cn.com/problems/binary-tree-paths/
> 以为只用了递归,其实还用了回溯
## 思路
# 257. 二叉树的所有路径
首先要知道遍历二叉树有两种遍历方式:二叉树深度优先遍历和二叉树广度优先遍历,那么每种遍历方式下还有不同的顺序。如下所示:
* 二叉树深度优先遍历
* 前序遍历: [0144.二叉树的前序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0144.二叉树的前序遍历.md)
* 后序遍历: [0145.二叉树的后序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0145.二叉树的后序遍历.md)
* 中序遍历: [0094.二叉树的中序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0094.二叉树的中序遍历.md)
* 二叉树广度优先遍历
* 层序遍历:[0102.二叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0102.二叉树的层序遍历.md)
给定一个二叉树,返回所有从根节点到叶子节点的路径。
这道题目要打印出根节点到叶子节点的所有路径,很明显广度优先遍历不合适,那么深度优先遍历中,应该选哪一种循序来遍历呢?
说明: 叶子节点是指没有子节点的节点。
**要打印路径,就要选前序遍历**,因为中序和后序遍历都不能打印出路径来。
示例:
<img src='../pics/257.二叉树的所有路径1.png' width=600> </img></div>
一些同学可能代码都写出来,而且都提交通过了,却不知道自己用了哪一种遍历,以及那种顺序来遍历的。
# 思路
前序遍历如题:
这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。
在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一一个路径在进入另一个路径。
前序遍历以及回溯的过程如图:
<img src='../pics/257.二叉树的所有路径.png' width=600> </img></div>
确定了是前序遍历,那么就是中左右的顺序。前序遍历 框架如下:
```
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
vec.push_back(cur->val); // 中 ,同时也是处理节点逻辑的地方
traversal(cur->left, vec); // 左
traversal(cur->right, vec); // 右
}
```
我们先使用递归的方式,来做前序遍历。**要知道递归和回溯就是一家的,本题也需要回溯。**
我们先使用递归的方式,来做前序遍历。那么要知道递归和回溯就是一家的,本题也需要回溯。
## 递归
1. 递归函数函数参数以及返回值
要传入节点记录每一条路径的path和存放结果集的result这里递归不需要返回值代码如下
要传入节点记录每一条路径的path和存放结果集的result这里递归不需要返回值代码如下
```
void traversal(TreeNode* cur, vector<int>& path, vector<string>& result)
@ -44,7 +36,7 @@ void traversal(TreeNode* cur, vector<int>& path, vector<string>& result)
2. 确定递归终止条件
写递归的时候都习惯了这么写:
写递归的时候都习惯了这么写:
```
if (cur == NULL) {
@ -63,29 +55,29 @@ if (cur->left == NULL && cur->right == NULL) {
}
```
为什么没有判断cur是否为空呢下文在讲解单层递归逻辑的时候会提到
为什么没有判断cur是否为空呢因为下面的逻辑可以控制空节点不入循环
再来看一下终止处理的逻辑。
这里使用vector<int> 结构来记录路径,所以要把路径转为string格式在把这个string 放进 result里。
这里使用vector<int> 结构path来记录路径,所以要把vector<int> 结构的path转为string格式在把这个string 放进 result里。
**那么为什么使用了vector<int> 结构来记录路径呢?** 因为在下面处理单层递归逻辑的时候要做回溯使用vector方便来做回溯。
那么有的同学问了,我看有些人的代码也没有回溯啊。
可能有的同学问了,我看有些人的代码也没有回溯啊。
其实是有的,只不过隐藏在 函数调用时的参数赋值里,下文我还会提到。
**其实是有回溯的,只不过隐藏在函数调用时的参数赋值里**,下文我还会提到。
这里我们先使用vector<int> 结构来记录路径,那么终止处理逻辑如下:
这里我们先使用vector<int>结构的path容器来记录路径,那么终止处理逻辑如下:
```
if (cur->left == NULL && cur->right == NULL) {
if (cur->left == NULL && cur->right == NULL) { // 遇到叶子节点
string sPath;
for (int i = 0; i < path.size() - 1; i++) {
for (int i = 0; i < path.size() - 1; i++) { // 将path里记录的路径转为string格式
sPath += to_string(path[i]);
sPath += "->";
}
sPath += to_string(path[path.size() - 1]);
result.push_back(sPath);
sPath += to_string(path[path.size() - 1]); // 记录最后一个节点(叶子节点)
result.push_back(sPath); // 收集一个路径
return;
}
```
@ -142,8 +134,6 @@ if (cur->right) {
那么本题整体代码如下:
## C++代码第一种写法
```
class Solution {
private:
@ -181,23 +171,22 @@ public:
}
};
```
如上的C++代码充分体现了回溯。
## C++代码第二种写法
接下来我介绍另一种写法,如下写法就是一个标准的前序遍历的过程。
那么如上代码可以精简成如下代码:
```
class Solution {
private:
void traversal(TreeNode* cur, string path, vector<string>& result) {
path += to_string(cur->val);
path += to_string(cur->val); // 中
if (cur->left == NULL && cur->right == NULL) {
result.push_back(path);
return;
}
if (cur->left) traversal(cur->left, path + "->", result);
if (cur->right) traversal(cur->right, path + "->", result);
if (cur->left) traversal(cur->left, path + "->", result); // 左
if (cur->right) traversal(cur->right, path + "->", result); // 右
}
public:
@ -212,14 +201,64 @@ public:
};
```
注意在函数定义的时候`void traversal(TreeNode* cur, string path, vector<string>& result)` ,定义的是`string path`,说明每次都是复制赋值
如上代码精简了不少,也隐藏了不少东西
那么在如上代码中,**貌似没有看到回溯的逻辑,其实不然,回溯就隐藏在`traversal(cur->left, path + "->", result);`中的 `path + "->"`。** 每次函数调用完path依然是没有+ 上"->" 的,这就是回溯了
注意在函数定义的时候`void traversal(TreeNode* cur, string path, vector<string>& result)` ,定义的是`string path`,每次都是复制赋值,不用使用引用,否则就无法做到回溯的效果
**综合以上,第二种写法更简洁,但是把很多重要的点隐藏在了代码细节里,第一种写法虽然代码多一些,但是每一个处理逻辑都完整的展现了出来。**
那么在如上代码中,**貌似没有看到回溯的逻辑,其实不然,回溯就隐藏在`traversal(cur->left, path + "->", result);`中的 `path + "->"`。** 每次函数调用完path依然是没有加上"->" 的,这就是回溯了。
至于还有非递归的方式,我在这篇题解[彻底吃透前中后序递归法(递归三部曲)和迭代法(不统一写法与统一写法)](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/solution/dai-ma-sui-xiang-lu-chi-tou-qian-zhong-hou-xu-de-d/) 已经彻底介绍过了,感兴趣的同学可以去看一看。
**综合以上,第二种递归的代码虽然精简但把很多重要的点隐藏在了代码细节里,第一种递归写法虽然代码多一些,但是把每一个逻辑处理都完整的展现了出来了。**
## 迭代法
至于非递归的方式,我们可以依然可以使用前序遍历的迭代方式来模拟遍历路径的过程,对该迭代方式不了解的同学,可以看文章[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg)和[二叉树:前中后序迭代方式的写法就不能统一一下么?](https://mp.weixin.qq.com/s/WKg0Ty1_3SZkztpHubZPRg)。
这里除了模拟递归需要一个栈,同时还需要一个栈来存放对应的遍历路径。
C++代码如下:
```
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
stack<TreeNode*> treeSt;// 保存树的遍历节点
stack<string> pathSt; // 保存遍历路径的节点
vector<string> result; // 保存最终路径集合
if (root == NULL) return result;
treeSt.push(root);
pathSt.push(to_string(root->val));
while (!treeSt.empty()) {
TreeNode* node = treeSt.top(); treeSt.pop(); // 取出节点 中
string path = pathSt.top();pathSt.pop(); // 取出该节点对应的路径
if (node->left == NULL && node->right == NULL) { // 遇到叶子节点
result.push_back(path);
}
if (node->right) { // 右
treeSt.push(node->right);
pathSt.push(path + "->" + to_string(node->right->val));
}
if (node->left) { // 左
treeSt.push(node->left);
pathSt.push(path + "->" + to_string(node->left->val));
}
}
return result;
}
};
```
当然使用java的同学可以直接定义一个成员变量为object的栈`Stack<Object> stack = new Stack<>();`,这样就不用定义两个栈了,都放到一个栈里就可以了。
# 总结
**本文我们开始初步涉及到了回溯,很多同学过了这道题目,可能都不知道自己其实使用了回溯,回溯和递归都是相伴相生的。**
我在第一版递归代码中,把递归与回溯的细节都充分的展现了出来,大家可以自己感受一下。
第二版递归代码对于初学者其实非常不友好,代码看上去简单,但是隐藏细节于无形。
最后我依然给出了迭代法。
对于本地充分了解递归与回溯的过程之后,有精力的同学可以在去实现迭代法。

View File

@ -0,0 +1,58 @@
## C++代码递归
```
class Solution {
public:
int maxLen = INT_MIN;
int maxleftValue;
void traversal(TreeNode* root, int leftLen) {
if (root->left == NULL && root->right == NULL) {
if (leftLen > maxLen) {
maxleftValue = root->val;
maxLen = leftLen;
}
return;
}
if (root->left) {
leftLen++;
traversal(root->left, leftLen);
leftLen--;
}
if (root->right) {
leftLen++;
traversal(root->right, leftLen);
leftLen--;
}
return;
}
int findBottomLeftValue(TreeNode* root) {
traversal(root, 0);
return maxleftValue;
}
};
```
## C++代码层序遍历
```
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
int result = 0;
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if (i == 0) result = node->val;
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return result;
}
};
```

View File

@ -23,3 +23,47 @@ public:
}
};
```
```
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if (p == NULL && q == NULL) return true;
if (p == NULL || q == NULL) return false;
queue<TreeNode*> que;
que.push(p); //
que.push(q); //
while (!que.empty()) { //
TreeNode* leftNode = que.front(); que.pop();
TreeNode* rightNode = que.front(); que.pop();
if (!leftNode && !rightNode) { //
continue;
}
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); //
}
return true;
}
bool isSubtree(TreeNode* s, TreeNode* t) {
stack<TreeNode*> st;
vector<int> result;
if (s == NULL) return false;
st.push(s);
while (!st.empty()) {
TreeNode* node = st.top(); // 中
st.pop();
if(isSameTree(node, t)) return true;
if (node->right) st.push(node->right); // 右(空节点不入栈)
if (node->left) st.push(node->left); // 左(空节点不入栈)
}
return false;
}
};
```