diff --git a/README.md b/README.md
index f039e36d..a975300e 100644
--- a/README.md
+++ b/README.md
@@ -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) |字符串 |简单| **模拟**|
diff --git a/pics/100.相同的树.png b/pics/100.相同的树.png
new file mode 100644
index 00000000..88da7600
Binary files /dev/null and b/pics/100.相同的树.png differ
diff --git a/pics/110.平衡二叉树2.png b/pics/110.平衡二叉树2.png
new file mode 100644
index 00000000..53d46be6
Binary files /dev/null and b/pics/110.平衡二叉树2.png differ
diff --git a/pics/257.二叉树的所有路径.png b/pics/257.二叉树的所有路径.png
index f46fc31a..0a0683e0 100644
Binary files a/pics/257.二叉树的所有路径.png and b/pics/257.二叉树的所有路径.png differ
diff --git a/pics/257.二叉树的所有路径1.png b/pics/257.二叉树的所有路径1.png
new file mode 100644
index 00000000..97452f51
Binary files /dev/null and b/pics/257.二叉树的所有路径1.png differ
diff --git a/problems/0100.相同的树.md b/problems/0100.相同的树.md
index 115ae411..4d9bb1e3 100644
--- a/problems/0100.相同的树.md
+++ b/problems/0100.相同的树.md
@@ -1,9 +1,115 @@
## 题目地址
https://leetcode-cn.com/problems/same-tree/
-## 思路
+(没写完)
-这道题目和101 基本是一样的
+# 100. 相同的树
+
+给定两个二叉树,编写一个函数来检验它们是否相同。
+
+如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
+
+
+
+# 思路
+
+在[二叉树:我对称么?](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
+* tree1,tree2都为空,对称,返回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;
diff --git a/problems/0110.平衡二叉树.md b/problems/0110.平衡二叉树.md
index a03815bd..09d5598a 100644
--- a/problems/0110.平衡二叉树.md
+++ b/problems/0110.平衡二叉树.md
@@ -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]
返回 false 。
-## 思路
+# 题外话
-分析一下这道题是不是和求深度很像,在[二叉树:看看这些树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中是求左右子树的最大深度,本题呢,是要求左右子树高度差绝对值不超过1。
+咋眼一看这道题目和[二叉树:看看这些树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)很像,其实有很大区别。
+这里强调一波概念:
+* 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。
+* 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。
+
+但leetcode中强调的深度和高度很明显是按照节点来计算的,如图:
+
+
+
+关于根节点的深度究竟是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 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 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 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 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」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。
diff --git a/problems/0257.二叉树的所有路径.md b/problems/0257.二叉树的所有路径.md
index 0772444b..3fcbe67d 100644
--- a/problems/0257.二叉树的所有路径.md
+++ b/problems/0257.二叉树的所有路径.md
@@ -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)
+给定一个二叉树,返回所有从根节点到叶子节点的路径。
-这道题目要打印出根节点到叶子节点的所有路径,很明显广度优先遍历不合适,那么深度优先遍历中,应该选哪一种循序来遍历呢?
+说明: 叶子节点是指没有子节点的节点。
-**要打印路径,就要选前序遍历**,因为中序和后序遍历都不能打印出路径来。
+示例:
+
-一些同学可能代码都写出来,而且都提交通过了,却不知道自己用了哪一种遍历,以及那种顺序来遍历的。
+# 思路
-前序遍历如题:
+这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。
+
+在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一一个路径在进入另一个路径。
+
+前序遍历以及回溯的过程如图:
-确定了是前序遍历,那么就是中左右的顺序。前序遍历 框架如下:
-```
-void traversal(TreeNode* cur, vector& 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& path, vector& result)
@@ -44,7 +36,7 @@ void traversal(TreeNode* cur, vector& path, vector& result)
2. 确定递归终止条件
-在写递归的时候都习惯了这么写:
+再写递归的时候都习惯了这么写:
```
if (cur == NULL) {
@@ -63,29 +55,29 @@ if (cur->left == NULL && cur->right == NULL) {
}
```
-为什么没有判断cur是否为空呢,下文在讲解单层递归逻辑的时候会提到。
+为什么没有判断cur是否为空呢,因为下面的逻辑可以控制空节点不入循环。
再来看一下终止处理的逻辑。
-这里使用vector 结构来记录路径,所以要把路径转为string格式,在把这个string 放进 result里。
+这里使用vector 结构path来记录路径,所以要把vector 结构的path转为string格式,在把这个string 放进 result里。
**那么为什么使用了vector 结构来记录路径呢?** 因为在下面处理单层递归逻辑的时候,要做回溯,使用vector方便来做回溯。
-那么有的同学问了,我看有些人的代码也没有回溯啊。
+可能有的同学问了,我看有些人的代码也没有回溯啊。
-其实是有的,只不过隐藏在 函数调用时的参数赋值里,下文我还会提到。
+**其实是有回溯的,只不过隐藏在函数调用时的参数赋值里**,下文我还会提到。
-这里我们先使用vector 结构来记录路径,那么终止处理逻辑如下:
+这里我们先使用vector结构的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& 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& result)` ,定义的是`string path`,说明每次都是复制赋值。
+如上代码精简了不少,也隐藏了不少东西。
-那么在如上代码中,**貌似没有看到回溯的逻辑,其实不然,回溯就隐藏在`traversal(cur->left, path + "->", result);`中的 `path + "->"`。** 每次函数调用完,path依然是没有+ 上"->" 的,这就是回溯了。
+注意在函数定义的时候`void traversal(TreeNode* cur, string path, vector& 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 binaryTreePaths(TreeNode* root) {
+ stack treeSt;// 保存树的遍历节点
+ stack pathSt; // 保存遍历路径的节点
+ vector 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