224、使用递归和非递归实现二叉树的前、中、后序遍历

使用递归来实现二叉树的前、中、后序遍历比较简单,直接给出代码,我们重点讨论非递归的实现。

class Node { public int value; public Node left; public Node right; public Node() { } public Node(int value) { this.value = value; }}public class Solution { //递归实现前序遍历 public void preOrderRecur(Node head){ if (head == null) return; System.out.println(head.value + " "); preOrderRecur(head.left); preOrderRecur(head.right); }  //递归实现中序遍历 public void inOrderRecur(Node head){ if (head == null) return; inOrderRecur(head.left); System.out.println(head.value + " "); inOrderRecur(head.right); } //递归实现后续遍历 public void posOrderRecur(Node head){ if (head == null) return; posOrderRecur(head.left); posOrderRecur(head.right); System.out.println(head.value + " "); }}

凡是可以使用递归可以解决的问题都可以使用非递归的方法来实现。这是因为递归方法无非就是利用函数栈来保存信息,如果用自己申请的数据结构来代替函数栈,也可以实现相同的功能。

用非递归的方法实现二叉树的先序遍历,具体过程如下:

1、申请一个新的栈,记为stack。然后将头结点head压入stack中;

2、从stack中弹出栈顶节点,记为cur,然后打印cur节点的值,再将节点cur的右孩子节点(如果不为空)先压入到stack中,最后将cur的左孩子节点(如果不为空)压入stack中。

3、不断重复步骤2,直到stack为空,过程全部结束。

下面举例说明整个过程,假如存在一颗二叉树如图所示:

  1. 节点1先入栈,然后弹出并打印。接下来把节点3压入栈,再把节点2压入栈,此时stack从顶到底依次为2、3;
  2. 节点2弹出并打印,把节点5压入栈,再把节点4压入栈,此时栈中从顶到底元素依次为4、5、3;
  3. 节点4弹出并打印,由于节点4没有孩子节点可以压入栈,所以此时栈中元素从顶到底依次为5、3;
  4. 节点5弹出并打印,由于节点5没有孩子节点可以压入栈,所以此时栈中元素仅有3;
  5. 节点3弹出并打印,先把节点7压入栈,再把节点6压入栈,此时栈中元素从顶到底依次为6、7;
  6. 节点6弹出并打印,由于节点6没有孩子节点可以压入栈,所以此时栈中元素仅有7;
  7. 节点7弹出并打印,由于节点7没有孩子节点可以压入栈,此时stack中为空,过程停止。

整个过程请参考如下代码:

 //不用递归实现前序遍历 public void preOrderUnRecur(Node head){ System.out.print("前序遍历:"); if (head != null){ Stack stack = new Stack<>(); stack.add(head); while (!stack.isEmpty()){ head = stack.pop(); System.out.print(head.value + " "); if (head.right != null){ stack.push(head.right); } if (head.left != null){ stack.push(head.left); } } } System.out.println(); }

用非递归的方式来实现二叉树的中序遍历,具体过程如下:

  1. 申请一个新的栈,记为stack。初始时,令变量cur = head;
  2. 先把cur节点压入栈中,对以cur节点为头结点的整颗子树来说,依次把左边界压入栈中,即不停的令cur= cur.left;然后重复此步骤;
  3. 不断重复步骤2,直到发现cur为空,此时从stack中弹出一个节点,记为node。打印node的值,并且让cur = node.right,然后继续重复步骤2;
  4. 当stack为空且cur为空时,整个过程停止。

还是以上图的二叉树为例子来说明整个过程:

  1. 初始时cur为节点1,将节点1压入栈,令cur= cur.left,此时cur变为节点2;
  2. cur为节点2,将节点2压入栈中,令cur = cur.left,即此时cur变为节点4;
  3. cur为节点4,将节点4压入栈中,令cur = cur.left,此时cur变为了null,此时栈中元素从顶到底依次为4、2、1;
  4. cur为null,从栈中弹出节点4(node)并打印,并令cur = node.right,cur仍然为null,此时stack中从顶到底依次为2、1;
  5. cur为null,从栈中弹出节点2(node)并打印,并令cur = node.right,此时cur变为节点5,此时栈中元素只有1;
  6. cur为节点5,将节点5压入stack中,并令cur = cur.left,此时cur变为null,栈中元素有5、1;
  7. cur为null,从栈中弹出节点5(node)并打印,并令cur = node.right,即cur仍为null,此时栈中元素为1;
  8. cur为null,从栈中弹出节点1(node)并打印,并令cur = node.right,即cur变为节点3,此时栈中为空;
  9. cur为节点3,将节点3压入栈中,令cur = cur.left,此时cur变为节点6,此时栈中仅有元素3;
  10. cur为节点6,将节点6压入栈中,令cur = cur.left,此时cur变为null,此时栈中有元素6、3;
  11. cur为null,弹出节点6(node)并打印,令cur = node.right,cur仍为null,此时栈中元素仅有3;
  12. cur为null,弹出节点3(node)并打印,令cur = node.right,cur变为节点7,此时栈中为空;
  13. cur为节点7,将节点7压入栈,令cur = cur.left,此时cur变为null,此时栈中仅有元素7;
  14. cur为null,弹出节点7(node)并打印,令cur = node.right,cur仍然为null,此时栈stack也为null。
  15. cur和stack同时为null,整个过程终止。

代码如下:

 //不用递归实现中序遍历 public void inOrderUnrecur(Node head){ System.out.println("in-Order:"); if (head != null){ Stack stack = new Stack<>(); while (!stack.isEmpty() || head != null){ if (head != null){ stack.push(head); head = head.left; }else { head = stack.pop(); System.out.print(head.value + " "); head = head.right; } } } System.out.println(); }

用非递归方式实现二叉树后序遍历有点麻烦。下面介绍使用两个栈来实现后序遍历的过程,具体步骤如下:

  1. 申请一个栈记为s1,然后将头结点压入s1中;
  2. 从s1中弹出的节点记为cur,然后依次将cur的左孩子和右孩子压入s1中;
  3. 在整个过程中,每一个从s1中弹出的节点都放进s2中;
  4. 不断重复步骤2和步骤3,直到s1为空,过程停止;
  5. 然后从s2中依次弹出节点并打印,打印的顺序就是后序遍历的顺序。

下面还是使用上面的图示二叉树来演示整个过程:

  1. 申请两个栈s1和s2,将头结点1压入s1中,然后再从s1中弹出节点1,再把节点1压入s2中,再把节点2和节点3一次压入s1,此时s1从栈顶到栈底元素为3、2;s2从栈顶到栈底只有1;
  2. 从s1中弹出节点3,节点3放入s2中,然后将节点3的两个孩子节点6和节点7一次压入s1,此时s1从栈顶到栈底元素依次为7、6、2;s2中为3、1;
  3. 从s1中弹出节点7,节点7放入s2中,由于节点7没有孩子,不需要往s1中添加任何元素,此时s1中保存6、2;s2中保存7、3、1;
  4. 从s1中弹出节点6,节点6放入s2中,由于节点6没有孩子,不需要往s1中添加任何元素,此时s1中保存2,s2中保存6、7、3、1;
  5. 从s1中弹出节点2,节点2放入s2中,然后将节点2的两个孩子节点4和节点5一次压入s1中,此时s1从栈顶到栈底元素为5、4;s2中存放的元素为2、6、7、3、1;
  6. 从s1中弹出节点5,节点5放入s2中,节点5无孩子节点,不需要往s1中添加任何元素,此时s1中保存4;s2中保存5、2、6、7、3、1;
  7. 从s1中弹出节点4,节点4放入s2中,节点4无孩子节点,不需要往s1中添加任何节点,此时s1中为空,s2中保存的元素从栈顶到栈底依次是4、5、2、6、7、3、1;
  8. 过程结束,依次弹出并打印s2中的节点即可。

通过如上过程我们知道,每棵子树的头结点都是先从s1中弹出,然后再把该节点的两个孩子节点按照先左再右的顺序依次压入s1,那么从s1弹出的顺序就是先右后左,所以从s1中弹出的顺序就是中、右、左。然后,s2重新收集的过程就是把s1的弹出顺序逆序,所以s2从栈顶到栈底的顺序就变成了左、右、中。

代码如下:

 //不用递归实现后续遍历 public void posOrderUnRecur(Node head){ System.out.println("pos-order: "); if (head != null){ Stack s1 = new Stack<>(); Stack s2 = new Stack<>(); s1.push(head); while (!s1.isEmpty()){ head = s1.pop(); s2.push(head); if (head.left != null){ s1.push(head.left); } if (head.right != null){ s1.push(head.right); } } while (!s2.isEmpty()){ System.out.print(s2.pop().value + " "); } } System.out.println(); }

java中二叉树_Java工程师面试1000题224-递归非递归实现二叉树前、中、后序遍历...相关推荐

  1. 二叉树前中后序遍历+刷题【中】【数据结构/初阶/C语言实现】

    文章目录 1. 二叉树基础操作 1.1 二叉树遍历 1.1.1 前序遍历 前序遍历(Pre-Order Traversal) 1.1.2 中序遍历 中序遍历(In-Order Traversal) 1 ...

  2. 二叉树的前,中,后序遍历(思路分析) [Java][数据结构]

    二叉树的前,中,后序遍历(思路分析) 前序遍历: 先输出父节点, 再遍历左子树和右子树 中序遍历: 先遍历左子树, 再输出父节点,再遍历右子树 后序遍历: 先遍历左子树,再遍历右子树,最后输出父节点 ...

  3. Java二叉树的前中后序遍历

    Java二叉树的前中后序遍历 1.前序遍历 1.1前序遍历概念 1.2前序遍历习题 2.中序遍历 2.1中序遍历概念 2.2中序遍历习题 3.后续遍历 3.1后序遍历概念 3.2后序遍历习题 大家好, ...

  4. 数据结构与算法(java):树-二叉树(二叉查找树(BST)、线索化二叉树、哈夫曼树、平衡二叉树【AVL】、二叉树的前中后序遍历)

    二叉树 1.定义 二叉树 就是度不超过2的树(每个结点最多只有两个子结点).如图 2.特殊二叉树 满二叉树 当二叉树的每一个层的结点树都达到最大值,则这个二叉树就是满二叉树. 完全二叉树 叶结点只能出 ...

  5. java二叉树合并_Java(树的前中后序遍历构造二叉树题型整合)前序和中序、中序和后序、前序和后序遍历序列构造二叉树算法整合归纳...

    前言 二叉树各种花里胡哨的算法题真的把我搞晕了,今天特地整理出一类有关二叉树的算法题,希望能帮助阅读到此文章的人,今后不再受此类题型的困扰. 一.题目类型 已知二叉树的两种遍历序列,请根据该序列构建二 ...

  6. Java~二叉树的前中后序遍历的几种方式(递归法,迭代法,标记法等)

    目录 一.结点的定义 二.递归法遍历二叉树 前序遍历 中序遍历 后序遍历 三.迭代(非递归)遍历二叉树 (1).迭代模拟法 前序遍历 中序遍历 后序遍历 (2).空指针标记法 前序遍历 中序遍历 后序 ...

  7. spring 事务隔离级别和传播行为_Java工程师面试1000题146-Spring数据库事务传播属性和隔离级别...

    146.简介一下Spring支持的数据库事务传播属性和隔离级别 介绍Spring所支持的事务和传播属性之前,我们先了解一下SpringBean的作用域,与此题无关,仅做一下简单记录. 在Spring中 ...

  8. java非递归遍历file树_Java语言实现非递归实现树的前中后序遍历总结

    前言 三种遍历的递归写法都很好写,所以总结一下非递归写法. 先贴一张图复习一下三种遍历方式就进入正文啦~ [注:本文所有代码实现中树的结点定义如下: public class Node { int v ...

  9. [Leedcode][JAVA][第94/144/145题][前中后序遍历][递归][迭代][二叉树]

    [问题描述][] 前序遍历 先输出当前结点的数据,再依次遍历输出左结点和右结点 中序遍历 先遍历输出左结点,再输出当前结点的数据,再遍历输出右结点 后续遍历 先遍历输出左结点,再遍历输出右结点,最后输 ...

最新文章

  1. 移动UI设计中的7种主要导航模式
  2. LSMW批处理使用方法(06)_步骤4、5
  3. listview刷新_Flutter NestedScrollView 滑动折叠头部下拉刷新效果
  4. 我就拜你为师的飞秋爱好者
  5. Django实现发邮件
  6. Android Studio 导入OpenCV 并调试运行face-detection例子
  7. 拓端tecdat|R语言用WinBUGS 软件对学术能力测验(SAT)建立层次(分层)贝叶斯模型
  8. 我要看的学习网站——php
  9. C语言小游戏——贪吃蛇
  10. 易简约个人产品中心网站源码html模板
  11. 计算机启动过程:MBR和BIOS
  12. 知识图谱从入门到应用——知识图谱的获取与构建:知识工程与知识获取
  13. kindle亚马逊个人文档不显示_Kindle 没东西看?一个插件就搞定!
  14. facenet 搭建人脸识别库
  15. Autoware1.14-摄像头目标检测YOLO-V3
  16. CRMEB小程序商城源码安装后,个人中心推广海报不显示处理方法!
  17. electron 打包后启动应用报错:Error: ENOENT: no such file or directory, open ‘xxx/manifest.json‘‘
  18. 词霸的每日一句的api接口
  19. 量化感知训练_《量化健身 动作精讲》:专业解读健身动作的秘密
  20. Xanadu:逐梦一个关于光量子计算的“新上都计划”

热门文章

  1. idea 调试 js
  2. python tkinter实例_Python tkinter模版代码实例
  3. 提示账户不被允许使用docker的情况
  4. eclipse报错找不到tools.jar
  5. Android开发笔记(一百零五)社会化分享SDK
  6. Android开发笔记(七十七)图片缓存算法
  7. 2db多少功率_话筒的灵敏度:-58dB+(-)2dB表示什么意思,数字大的好,还是小的好呢?...
  8. 根据条件返回相应值 decode(条件,值1,翻译值1,值2,翻译值2,...值n,翻译值n,缺省值)...
  9. jsp内置对象作业3-application用户注册
  10. zlib源码导读[转]