二叉树:前中后序迭代方式的写法就不能统一一下么?
本文:https://github.com/youngyangyang04/leetcode-master已经收录,里面还有leetcode刷题攻略、各个类型经典题目刷题顺序、思维导图,可以fork到自己仓库,有空看一看一定会有所收获,如果对你有帮助也给一个star支持一下吧!
此时我们在二叉树:一入递归深似海,从此offer是路人中用递归的方式,实现了二叉树前中后序的遍历。
在二叉树:听说递归能做的,栈也能做!中用栈实现了二叉树前后中序的迭代遍历(非递归)。
之后我们发现「迭代法实现的先中后序,其实风格也不是那么统一,除了先序和后序,有关联,中序完全就是另一个风格了,一会用栈遍历,一会又用指针来遍历。」
实践过的同学,也会发现使用迭代法实现先中后序遍历,很难写出统一的代码,不像是递归法,实现了其中的一种遍历方式,其他两种只要稍稍改一下节点顺序就可以了。
其实「针对三种遍历方式,使用迭代法是可以写出统一风格的代码!」
「重头戏来了,接下来介绍一下统一写法。」
我们以中序遍历为例,在二叉树:听说递归能做的,栈也能做!中提到说使用栈的话,「无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况」。
「那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。」
如何标记呢,「就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。」 这种方法也可以叫做标记法。
迭代法中序遍历
中序遍历代码如下:(详细注释)
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数组就是最终结果集。
可以看出我们将访问的节点直接加入到栈中,但如果是处理的节点则后面放入一个空节点, 这样只有空节点弹出的时候,才将下一个节点放进结果集。
此时我们再来看前序遍历代码。
迭代法前序遍历
迭代法前序遍历代码如下:(「注意此时我们和中序遍历相比仅仅改变了两行代码的顺序」)
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;
}
};
迭代法后序遍历
后续遍历代码如下:(「注意此时我们和中序遍历相比仅仅改变了两行代码的顺序」)
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,哈工大师兄,先后在腾讯和百度从事技术研发多年,利用工作之余重刷leetcode。
我的B站(里面有我讲解的算法视频以及编程相关知识):https://space.bilibili.com/525438321
我的github:https://github.com/youngyangyang04
更多 精彩算法文章尽在:代码随想录,关注后,回复「Java」「C++」「python」「简历模板」等等,有我整理多年的学习资料,可以加我 微信,备注「个人简介」+「组队刷题」,拉你进入刷题群(无任何广告,纯个人分享),每天一道经典题目分析,我选的每一道题目都不是孤立的,而是由浅入深一脉相承的,如果跟住节奏每篇连续着看,定会融会贯通。
- 图解javascript this指向什么?
- 2017/6/8-python正则表达式的使用
- 洛谷P1306 斐波那契公约数
- Angular开发实践(二):HRM运行机制
- Angular开发实践(一):环境准备及框架搭建
- 洛谷P2818 天使的起誓
- 连续子数组的最大和
- 10.25解题报告
- React第三方组件5(状态管理之Redux的使用①简单使用)
- JavaScript设计模式与开发实践 - 观察者模式
- React第三方组件4(状态管理之Reflux的使用⑤异步操作)
- Leetcode-Easy 804. Unique Morse Code Words
- JavaScript设计模式与开发实践 - 策略模式
- 二叉树的深度
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- UNIX时间戳和北京时间的相互转换
- R语言对苏格兰独立民意调查的Meta分析
- 案例:归档自动清理脚本失效及连带影响
- R语言中固定与随机效应Meta分析 - 效率和置信区间覆盖
- R语言使用 LOWESS技术图分析逻辑回归中的函数形式
- R语言在逻辑回归中求R square R方
- R语言Poisson回归的拟合优度检验
- R语言ROC曲线下的面积-评估逻辑回归中的歧视
- 东芝MCU实现位带操作
- 单向链表的一点儿感悟
- rt-thread的内存管理分析
- R平方/相关性取决于预测变量的方差
- stata具有异方差误差的区间回归
- R语言用于线性回归的稳健方差估计
- 用SAS进行泊松,零膨胀泊松和有限混合Poisson模型分析