一种二叉树非递归遍历的简单写法

目录

  • 一种二叉树非递归遍历的简单写法
    • 先序遍历
    • 中序遍历
    • 后序遍历

二叉树的遍历是数据结构中非常基础的一个知识点,也是面试手撕代码环节的一个常见题目。这个问题的递归写法是比较简单的,但是如果面试官要求使用非递归写法,那么恭喜你同学,你被卷了。

这里提供一种简单的写法,我这里的简单,是指分析方法简单,而不是简洁(简洁的代码要看懂未必简单)。只要把循环中可能出现的各种情况分析清楚,那么写代码时只是把一堆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;
}

一种二叉树非递归遍历的简单写法相关推荐

  1. 二叉树非递归遍历(模版)

    读完本篇内容大约花费您7分钟时间 本文主要讲解二叉树非递归遍历,由于是非递归遍历,所以需要用到栈stack,我们如果仔细考虑递归遍历的代码,就能明白非递归种栈的应用. 由于几种遍历方式只是在处理中间节 ...

  2. C++版二叉树非递归遍历

    C++版二叉树非递归遍历 文章目录 C++版二叉树非递归遍历 一.二叉树前序遍历 二.二叉树中序遍历 三.二叉树后序遍历 一.二叉树前序遍历 /*** Definition for a binary ...

  3. 二叉树非递归遍历的一点理解

    二叉树是我们必须要了解的一个数据结构,针对这个数据结构我们可以衍生出好多知识. 主要是有几种遍历方式,前序遍历,中序遍历,后续遍历,层次遍历. 下面我们根据这个图来详细的说一下这几种非递归遍历的思想与 ...

  4. 二叉树非递归遍历的经典求解

    #include <stdio.h> #include <stdlib.h> typedef int datatype; typedef struct node {dataty ...

  5. 二叉树非递归遍历实现(Java)

    首先理解一下二叉树节点结构.left指向左节点,right指向右节点,val为节点中的值. class TreeNode {int val;TreeNode left;TreeNode right;p ...

  6. 数据结构_二叉树非递归遍历

    package zz;import java.util.Stack;/*** 二叉树中的二叉搜索树,即一个节点的左子节点关键值小于这个节点,右子节点的关键值大于这个节点* * @author Admi ...

  7. 对于二叉树三种非递归遍历方式的理解

    利用栈实现二叉树的先序,中序,后序遍历的非递归操作 栈是一种先进后出的数据结构,其本质应是记录作用,支撑回溯(即按原路线返回):因此,基于其的二叉树遍历操作深刻的体现了其特性: 若后续的输入和其前面的 ...

  8. 【转】更简单的非递归遍历二叉树的方法

    [转]更简单的非递归遍历二叉树的方法 解决二叉树的很多问题的方案都是基于对二叉树的遍历.遍历二叉树的前序,中序,后序三大方法算是计算机科班学生必写代码了.其递归遍历是人人都能信手拈来,可是在手生时写出 ...

  9. 更简单的非递归遍历二叉树

    解决二叉树的很多问题的方案都是基于对二叉树的遍历.遍历二叉树的前序,中序,后序三大方法算是计算机科班学生必写代码了.其递归遍历是人人都能信手拈来,可是在手生时写出非递归遍历恐非易事.正因为并非易事,所 ...

最新文章

  1. Python3之set, frozenset记录
  2. VC++套接字、数据库、文件读写综合应用-客户端读取文件套接字接收服务端写入数据库
  3. 【android-tips】如何在android应用中插入百度广告(附源码)
  4. POJ 2492 A Bug's Life 带权并查集
  5. 关于java中Scanner的next和nextLine
  6. java避免内存泄露_Java防止非静态内部类内存泄漏
  7. MyEclipse工具的优化使用
  8. Java进阶篇设计模式之十三 ---- 观察者模式和空对象模式
  9. python输入单词显示长度_Python按长度打印单词
  10. shiro的anon部分失效
  11. 为不喝的朋友准备的!
  12. flutter 控制iOS设备屏幕可旋转支持方向
  13. 如何提高自己的编码水平
  14. 计算机基础及excel,S102-计算机基础(06)-EXCEL简介及基本操作
  15. 设置ListView中的所有Item均不可点击
  16. c语言莹源码,exp5/wc/client.c · 张雪莹20175227/ISSDF - Gitee.com
  17. 20155227辜彦霖《基于Cortex-M4的UCOSIII的应用》课程设计个人报告
  18. LWUIT的List应用系列(一)List基础
  19. matlab绝对均值,在K-Means算法中使用绝对皮尔逊相关作为距离(MATLAB)
  20. CW32系列模数转换器(ADC)

热门文章

  1. Openbiz Cubi 企业级应用程序开发(一)
  2. JS window事件全集解析 (转载)
  3. 面试官系统精讲Java源码及大厂真题 - 29 押宝线程源码面试题
  4. oracle-xe-11.2.0-1.0.x86_64安装教程
  5. 《Spring技术内幕(第2版)》PDF 国内经典分析spring源代码
  6. Java 基础数据结构介绍
  7. 【记录】jenkins 安装及环境配置(一)
  8. 编写高质量代码的50条黄金守则-Day 01(首选隐式类型转换)
  9. java 快速排序 递归_Java递归快速入门
  10. 屏幕阅读器安全吗_如何为屏幕阅读器设计网站布局