一种二叉树非递归遍历的简单写法
一种二叉树非递归遍历的简单写法
目录
- 一种二叉树非递归遍历的简单写法
- 先序遍历
- 中序遍历
- 后序遍历
二叉树的遍历是数据结构中非常基础的一个知识点,也是面试手撕代码环节的一个常见题目。这个问题的递归写法是比较简单的,但是如果面试官要求使用非递归写法,那么恭喜你同学,你被卷了。
这里提供一种简单的写法,我这里的简单,是指分析方法简单,而不是简洁(简洁的代码要看懂未必简单)。只要把循环中可能出现的各种情况分析清楚,那么写代码时只是把一堆if else
填进去而已。
我们的分析方法的要点是:在每一轮循环中,把握住当前节点的状态,以及其左、右子树是否为空。下面我们将依次对先序、中序和后序遍历进行讨论。
先序遍历
针对当前节点,其左右子树无非下表列的四种情况。当右子树为空时,当前节点无需入栈,因为我们不需要再回头访问它的右子树了。当左子树为空但右子树非空时,当前节点也无需入栈,因为我们下一个就要访问它的右子树了。
同时我们需要一个标志变量,记录下当前的节点是否是从栈里弹出来的,如果是,那么不输出当前节点,并开始访问其右子树。
当前节点不是从栈里弹出来的 | 当前节点是从栈里弹出来的 | |
---|---|---|
左非空,右非空 | 输出当前节点,当前节点入栈,置左子树为当前节点 | 不输出当前节点,当前节点不入栈,置右子树为当前节点 |
左非空,右空 | 输出当前节点,当前节点不入栈,置左子树为当前节点 | 不存在这种情况,因为没有入栈 |
左空,右非空 | 输出当前节点,当前节点不入栈,置右子树为当前节点 | 不存在这种情况,因为没有入栈 |
左空,右空 | 输出当前节点,当前节点不入栈,从栈中弹出节点置为当前节点 | 不存在这种情况,因为没有入栈 |
好了,有了上面这个表,我们只需要把这个逻辑填进循环体就可以了,不过是一堆if else
。
循环终止的条件是什么?栈中保存的是我们需要回头访问其右子树的节点。栈为空,说明整个二叉树中此时已经不存在还需要访问的右子树了,而按照先序遍历的顺序,右子树是最后才访问的,因此整个二叉树中已经不存在需要访问的节点了。所以栈为空时循环结束。
代码如下:
std::vector<int> PreOrderTraversal(TreeNode* root) {std::vector<int> ans;if(!root) return ans;std::stack<TreeNode*> stack_node;TreeNode* cur_node = root;bool cur_is_poped = false;while(true) {if(!cur_is_poped) {if(cur_node->left) {if(cur_node->right) stack_node.push(cur_node);ans.push_back(cur_node->val);cur_node = cur_node->left;} else if(cur_node->right) {ans.push_back(cur_node->val);cur_node = cur_node->right;} else {ans.push_back(cur_node->val);if(stack_node.empty()) break;cur_node = stack_node.top();stack_node.pop();cur_is_poped = true;}} else {cur_node = cur_node->right;cur_is_poped = false;}}return ans;
}
中序遍历
中序遍历的情况相对简单。按照先序遍历的分析方法,我们可以得到类似的表格。
左非空,右非空 | 输出当前节点,置右子树为当前节点,下一轮循环需对右子树进行遍历 |
左非空,右空 | 输出当前节点,从栈中弹出节点置为当前节点 |
左空,右非空 | 输出当前节点,置右子树为当前节点,下一轮循环需对右子树进行遍历 |
左空,右空 | 输出当前节点,从栈中弹出节点置为当前节点 |
代码如下:
std::vector<int> InOrderTraversal(TreeNode* root) {std::vector<int> ans;if(!root) return ans;std::stack<TreeNode*> s;TreeNode* cur_node = root;bool cur_is_poped = false;while(true) {if(!cur_is_poped) {while(cur_node) {s.push(cur_node);cur_node = cur_node->left;}}if(s.empty()) break;cur_node = s.top();s.pop();ans.push_back(cur_node->val);cur_is_poped = true;if(cur_node->right) {cur_node = cur_node->right;cur_is_poped = false;}}return ans;
}
可以看出,所有的节点都是先入栈,再出栈并输出的。因此循环中止的条件,仍然是栈为空。
后序遍历
后序遍历比中序遍历复杂的地方在于:中序遍历时,栈顶元素必然被出栈并输出;但是后序遍历需要先访问其右子树,然后再输出该节点。我们需要一个标记变量,标记栈中的元素是否已经在栈顶出现过了,如果没有出现过,且其右子树不为空,那我们需要先访问其右子树。如果出现过,那么直接输出。
当前节点未在栈顶出现过 | 当前节点已在栈顶出现过 | |
---|---|---|
左非空,右非空 | 不输出当前节点,当前节点不出栈,置右子树为当前节点 | 输出当前节点,当前节点出栈,从栈中弹出节点置为当前节点 |
左非空,右空 | 输出当前节点,当前节点出栈,从栈中弹出节点置为当前节点 | 不存在,因为直接出栈了 |
左空,右非空 | 不输出当前节点,当前节点不出栈,置右子树为当前节点 | 输出当前节点,当前节点出栈,从栈中弹出节点置为当前节点 |
左空,右空 | 输出当前节点,当前节点出栈,从栈中弹出节点置为当前节点 | 不存在,因为直接出栈了 |
std::vector<int> PostOrderTraversal(TreeNode* root) {std::vector<int> ans;if(!root) return ans;using NodePair = std::pair<TreeNode*, bool>;std::stack<NodePair> s;NodePair cur_node_pair;TreeNode* cur_node = root;while(true) {while(cur_node) {s.push(std::make_pair(cur_node, false));cur_node = cur_node->left;}if(s.empty()) break;cur_node_pair = s.top();if(!cur_node_pair.second) { //当前节点未在栈顶出现过if(cur_node_pair.first->right) { cur_node = cur_node_pair.first->right;cur_node_pair.second = true;s.top() = cur_node_pair;} else {ans.push_back(cur_node_pair.first->val);s.pop();}} else { //当前节点已在栈顶出现过ans.push_back(cur_node_pair.first->val);s.pop();}}return ans;
}
一种二叉树非递归遍历的简单写法相关推荐
- 二叉树非递归遍历(模版)
读完本篇内容大约花费您7分钟时间 本文主要讲解二叉树非递归遍历,由于是非递归遍历,所以需要用到栈stack,我们如果仔细考虑递归遍历的代码,就能明白非递归种栈的应用. 由于几种遍历方式只是在处理中间节 ...
- C++版二叉树非递归遍历
C++版二叉树非递归遍历 文章目录 C++版二叉树非递归遍历 一.二叉树前序遍历 二.二叉树中序遍历 三.二叉树后序遍历 一.二叉树前序遍历 /*** Definition for a binary ...
- 二叉树非递归遍历的一点理解
二叉树是我们必须要了解的一个数据结构,针对这个数据结构我们可以衍生出好多知识. 主要是有几种遍历方式,前序遍历,中序遍历,后续遍历,层次遍历. 下面我们根据这个图来详细的说一下这几种非递归遍历的思想与 ...
- 二叉树非递归遍历的经典求解
#include <stdio.h> #include <stdlib.h> typedef int datatype; typedef struct node {dataty ...
- 二叉树非递归遍历实现(Java)
首先理解一下二叉树节点结构.left指向左节点,right指向右节点,val为节点中的值. class TreeNode {int val;TreeNode left;TreeNode right;p ...
- 数据结构_二叉树非递归遍历
package zz;import java.util.Stack;/*** 二叉树中的二叉搜索树,即一个节点的左子节点关键值小于这个节点,右子节点的关键值大于这个节点* * @author Admi ...
- 对于二叉树三种非递归遍历方式的理解
利用栈实现二叉树的先序,中序,后序遍历的非递归操作 栈是一种先进后出的数据结构,其本质应是记录作用,支撑回溯(即按原路线返回):因此,基于其的二叉树遍历操作深刻的体现了其特性: 若后续的输入和其前面的 ...
- 【转】更简单的非递归遍历二叉树的方法
[转]更简单的非递归遍历二叉树的方法 解决二叉树的很多问题的方案都是基于对二叉树的遍历.遍历二叉树的前序,中序,后序三大方法算是计算机科班学生必写代码了.其递归遍历是人人都能信手拈来,可是在手生时写出 ...
- 更简单的非递归遍历二叉树
解决二叉树的很多问题的方案都是基于对二叉树的遍历.遍历二叉树的前序,中序,后序三大方法算是计算机科班学生必写代码了.其递归遍历是人人都能信手拈来,可是在手生时写出非递归遍历恐非易事.正因为并非易事,所 ...
最新文章
- Python3之set, frozenset记录
- VC++套接字、数据库、文件读写综合应用-客户端读取文件套接字接收服务端写入数据库
- 【android-tips】如何在android应用中插入百度广告(附源码)
- POJ 2492 A Bug's Life 带权并查集
- 关于java中Scanner的next和nextLine
- java避免内存泄露_Java防止非静态内部类内存泄漏
- MyEclipse工具的优化使用
- Java进阶篇设计模式之十三 ---- 观察者模式和空对象模式
- python输入单词显示长度_Python按长度打印单词
- shiro的anon部分失效
- 为不喝的朋友准备的!
- flutter 控制iOS设备屏幕可旋转支持方向
- 如何提高自己的编码水平
- 计算机基础及excel,S102-计算机基础(06)-EXCEL简介及基本操作
- 设置ListView中的所有Item均不可点击
- c语言莹源码,exp5/wc/client.c · 张雪莹20175227/ISSDF - Gitee.com
- 20155227辜彦霖《基于Cortex-M4的UCOSIII的应用》课程设计个人报告
- LWUIT的List应用系列(一)List基础
- matlab绝对均值,在K-Means算法中使用绝对皮尔逊相关作为距离(MATLAB)
- CW32系列模数转换器(ADC)
热门文章
- Openbiz Cubi 企业级应用程序开发(一)
- JS window事件全集解析 (转载)
- 面试官系统精讲Java源码及大厂真题 - 29 押宝线程源码面试题
- oracle-xe-11.2.0-1.0.x86_64安装教程
- 《Spring技术内幕(第2版)》PDF 国内经典分析spring源代码
- Java 基础数据结构介绍
- 【记录】jenkins 安装及环境配置(一)
- 编写高质量代码的50条黄金守则-Day 01(首选隐式类型转换)
- java 快速排序 递归_Java递归快速入门
- 屏幕阅读器安全吗_如何为屏幕阅读器设计网站布局