156 lines
6.1 KiB
Markdown
156 lines
6.1 KiB
Markdown
<p align="center">
|
||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" 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://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||
</p>
|
||
|
||
|
||
|
||
# 二叉树的统一迭代法
|
||
|
||
> 统一写法是一种什么感觉
|
||
|
||
此时我们在[二叉树:一入递归深似海,从此offer是路人](https://mp.weixin.qq.com/s/PwVIfxDlT3kRgMASWAMGhA)中用递归的方式,实现了二叉树前中后序的遍历。
|
||
|
||
在[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg)中用栈实现了二叉树前后中序的迭代遍历(非递归)。
|
||
|
||
之后我们发现**迭代法实现的先中后序,其实风格也不是那么统一,除了先序和后序,有关联,中序完全就是另一个风格了,一会用栈遍历,一会又用指针来遍历。**
|
||
|
||
实践过的同学,也会发现使用迭代法实现先中后序遍历,很难写出统一的代码,不像是递归法,实现了其中的一种遍历方式,其他两种只要稍稍改一下节点顺序就可以了。
|
||
|
||
其实**针对三种遍历方式,使用迭代法是可以写出统一风格的代码!**
|
||
|
||
**重头戏来了,接下来介绍一下统一写法。**
|
||
|
||
我们以中序遍历为例,在[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg)中提到说使用栈的话,**无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况**。
|
||
|
||
**那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。**
|
||
|
||
如何标记呢,**就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。** 这种方法也可以叫做标记法。
|
||
|
||
## 迭代法中序遍历
|
||
|
||
中序遍历代码如下:(详细注释)
|
||
|
||
```C++
|
||
class Solution {
|
||
public:
|
||
vector<int> inorderTraversal(TreeNode* root) {
|
||
vector<int> result;
|
||
stack<TreeNode*> st;
|
||
if (root != NULL) st.push(root);
|
||
while (!st.empty()) {
|
||
TreeNode* node = st.top();
|
||
if (node != NULL) {
|
||
st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
|
||
if (node->right) st.push(node->right); // 添加右节点(空节点不入栈)
|
||
|
||
st.push(node); // 添加中节点
|
||
st.push(NULL); // 中节点访问过,但是还没有处理,加入空节点做为标记。
|
||
|
||
if (node->left) st.push(node->left); // 添加左节点(空节点不入栈)
|
||
} else { // 只有遇到空节点的时候,才将下一个节点放进结果集
|
||
st.pop(); // 将空节点弹出
|
||
node = st.top(); // 重新取出栈中元素
|
||
st.pop();
|
||
result.push_back(node->val); // 加入到结果集
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
};
|
||
```
|
||
|
||
看代码有点抽象我们来看一下动画(中序遍历):
|
||
|
||

|
||
|
||
动画中,result数组就是最终结果集。
|
||
|
||
可以看出我们将访问的节点直接加入到栈中,但如果是处理的节点则后面放入一个空节点, 这样只有空节点弹出的时候,才将下一个节点放进结果集。
|
||
|
||
此时我们再来看前序遍历代码。
|
||
|
||
## 迭代法前序遍历
|
||
|
||
迭代法前序遍历代码如下: (**注意此时我们和中序遍历相比仅仅改变了两行代码的顺序**)
|
||
|
||
```C++
|
||
class Solution {
|
||
public:
|
||
vector<int> preorderTraversal(TreeNode* root) {
|
||
vector<int> result;
|
||
stack<TreeNode*> st;
|
||
if (root != NULL) st.push(root);
|
||
while (!st.empty()) {
|
||
TreeNode* node = st.top();
|
||
if (node != NULL) {
|
||
st.pop();
|
||
if (node->right) st.push(node->right); // 右
|
||
if (node->left) st.push(node->left); // 左
|
||
st.push(node); // 中
|
||
st.push(NULL);
|
||
} else {
|
||
st.pop();
|
||
node = st.top();
|
||
st.pop();
|
||
result.push_back(node->val);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
};
|
||
```
|
||
|
||
## 迭代法后序遍历
|
||
|
||
后续遍历代码如下: (**注意此时我们和中序遍历相比仅仅改变了两行代码的顺序**)
|
||
|
||
```C++
|
||
class Solution {
|
||
public:
|
||
vector<int> postorderTraversal(TreeNode* root) {
|
||
vector<int> result;
|
||
stack<TreeNode*> st;
|
||
if (root != NULL) st.push(root);
|
||
while (!st.empty()) {
|
||
TreeNode* node = st.top();
|
||
if (node != NULL) {
|
||
st.pop();
|
||
st.push(node); // 中
|
||
st.push(NULL);
|
||
|
||
if (node->right) st.push(node->right); // 右
|
||
if (node->left) st.push(node->left); // 左
|
||
|
||
} else {
|
||
st.pop();
|
||
node = st.top();
|
||
st.pop();
|
||
result.push_back(node->val);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
};
|
||
```
|
||
|
||
## 总结
|
||
|
||
此时我们写出了统一风格的迭代法,不用在纠结于前序写出来了,中序写不出来的情况了。
|
||
|
||
但是统一风格的迭代法并不好理解,而且想在面试直接写出来还有难度的。
|
||
|
||
所以大家根据自己的个人喜好,对于二叉树的前中后序遍历,选择一种自己容易理解的递归和迭代法。
|
||
|
||
------------------------
|
||
|
||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||
|
||

|
||
|
||
|