Java语言实现非递归实现树的前中后序遍历总结

前言

三种遍历的递归写法都很好写,所以总结一下非递归写法。

先贴一张图复习一下三种遍历方式就进入正文啦~

【注:本文所有代码实现中树的结点定义如下:

public class Node {
  int val;
  Node left;
  Node right;
  Node parent;
  Node() {}
  Node(int val) {
    this.val = val;
  }
}

1.前序遍历

实现思路:

前序遍历的顺序是:根结点 -> 左孩子 -> 右孩子

借助一个栈结构先将根结点压入栈,然后循环每次取出栈顶元素,直到栈为空。如果当前结点有右孩子就先将右孩子压入栈中,如果当前结点有左孩子就将左孩子压入栈中,这里注意顺序,因为栈的结构是先进后出的,为了保证先遍历到左孩子就应该后压左孩子~

代码实现:

【代码使用直接输出的方式打印中序遍历结果,也可以放入一个list中存储~】

public void preOrder(Node head) {
    System.out.println("pre-order:");
    if(head != null) {
      Stack<Node> stack = new Stack<>();
      stack.push(head);
      while(!stack.isEmpty()) {
        head = stack.pop();
        System.out.print(head.val + " ");
        if (head.right != null)
          stack.push(head.right);
        if (head.left != null)
          stack.push(head.left);
      }
    }
  }

2.中序遍历

实现思路:

中序遍历的顺序:左孩子 -> 根结点 -> 右孩子

借助栈结构,将当前结点的左孩子依次入栈直到没有左孩子了就弹出栈顶元素并打印,如果弹出的结点有右孩子就将右孩子的左边界依次入栈,循环…

比如文章开始的那个图中,先将A,B,D依次入栈,此时栈顶元素是D,弹出并打印,D结点有右孩子,将D的右孩子的左边界依次入栈,压入结点G,结点G没有左孩子所以弹出并打印G,此时栈顶元素是B,循环…直到栈中为空且当前结点为空时遍历结束。

代码实现:

public static void inOrderTraverse(Node head) {
    System.out.println("in-order:");
    if(head != null) {
      Stack<Node> 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.val + " ");
          head = head.right;
        }
      }
    }
  }

3.后序遍历

实现思路:

后序遍历的顺序:左孩子 -> 右孩子 -> 根结点

仔细想一下,打印每个结点需要访问根结点三次,先从根结点开始找到左孩子,返回再找到右孩子,再返回到根结点,需要访问根结点三次,直接按照当前顺序进行遍历不知如何下手,那么我们可以换一个角度去考虑。

栈结构是先进后出的,那我们不妨借助一个栈先存储 根结点 -> 右孩子 -> 左孩子 的结果,再将其依次弹出就是后序遍历的顺序了。

那实现 根结点 -> 右孩子 -> 左孩子 的方法就很简单了,回忆一下刚才说的前序遍历的方式,前序遍历是先要左孩子,就后压入左孩子,反之,将前序遍历的逻辑改写为:弹出每个栈顶结点作为当前结点并存储到一个辅助栈中,如果当前结点有左孩子就先压入左孩子,如果有右孩子就后压入右孩子,每次取栈顶弹出到辅助栈中。最后将得到的辅助栈中元素依次弹出得到的就是后序遍历的结果。

代码实现:

public static void posOrderTraverse(Node head) {
    System.out.println("pos-order");
    if(head != null) {
      Stack<Node> stack1 = new Stack<>();
      Stack<Node> stack2 = new Stack<>();   // 辅助栈,存储 根 -> 右 -> 左 的结果
      stack1.push(head);
      while(!stack1.isEmpty()) {
        head = stack1.pop();
        stack2.push(head);
        // 有左孩子就先压入左孩子
        if(head.left != null)
          stack1.push(head.left);
        // 有右孩子就后压入右孩子
        if(head.right != null)
          stack1.push(head.right);
      }
      // 逆序打印 根 -> 右 -> 左 的结果,就是后序遍历的结果
      while(!stack2.isEmpty())
        System.out.print(stack2.pop().val + " ");
    }
  }

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。如果你想了解更多相关内容请查看下面相关链接

(0)

相关推荐

  • java连连看游戏菜单设计

    本文实例为大家分享了java连连看游戏菜单的具体实现代码,供大家参考,具体内容如下 先写GUI. 首先初始化框架,菜单,按钮,需要把菜单和按钮都添加在框架中.注意添加的顺序,首先要设置菜单,再设置框架,再设置按钮,如果交换了设置菜单和框架的顺序,会导致菜单显示不出,被框架挡住.对菜单设置了三个选项,第一个选项有五个下拉按键,用循环添加,第二个和第三个选项的下拉按键直接添加. GUI代码如下: package gui; import java.awt.Font; import javax.swin

  • Java多边形重心计算

    多边形重心计算 三角形重心 顶点为a,b,c的三角形重心为x = (xa + xb + xc) / 3,y = (ya + yb + yc) / 3 多边形重心 x = (x1w1 + x2w2 + - + xnwn)/W y = (y1w1 + y2w2 + - + ynwn)/W import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Polygon; import org.locationt

  • Java二叉树的遍历思想及核心代码实现

    二叉树在计算机中的存储方式往往线性结构,线性存储分为顺序存储和链式存储,将二叉树按层序编号. 顺序结构:按编号的顺序进行存储,对于完全二叉树而言,顺序存储可以反映二叉树的逻辑,但是对于大多数的二叉树则无法反映其逻辑关系,不过可以用 ^ 来代替不存在的结点,但是如果这个树是一个右斜树,就非常浪费存储空间.所以二叉树的存储形式一般为链式存储结构. 链式存储:每一个结点都分有一个数据域(data)和两个指针域(lchild和rchild),指针域分别指向左孩子和右孩子,若为空则为null.遍历方式有四

  • Java经典快排思想以及快排的改进讲解

    一.经典快排思想 前提条件:给定一个无序数组arr 取这个数组最后一个数 num 作为标准,将前面部分的数分为两部分,使得<=num的部分在左边,>num的数在右边: 然后将最后一个数和>num部分的第一个数进行交换,就使得原本在数组最后位置的num找到了正确的位置,它的左边都是比它小的以及和它一样的数,右边都是比它大的数 回到1,进行递归或迭代,使得所有的数都找到正确的位 二.通过荷兰国旗问题改进快排 什么是荷兰国旗问题? 已知一个整形数组arr,和一个整数num,请把小于num的数放

  • 浅谈Java 8 新增函数式接口到底是什么

    从 Java 8 开始便出现了函数式接口(Functional Interface,以下简称FI) 定义为: 如果一个接口只有唯一的一个抽象接口,则称之为函数式接口.为了保证接口符合 FI ,通常会在接口类上添加 @FunctionalInterface 注解.理解了函数式接口可以为 Java 函数式编程打下基础,最终可通过运用函数式编程极大地提高编程效率. 函数式接口 (Functional Interface) 就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口. 函数式接口可以对

  • Java排序算法之堆排思想及代码实现

    在介绍堆排序前,我们需要了解一下一种数据结构 -- 顶堆. 什么是顶堆? 它是一颗完全二叉树,顶堆有大顶堆和小顶堆两种.所谓大顶堆就是在这颗完全二叉树中,任何一颗子树都满足:父结点的值 > 孩子结点的值:小顶堆则相反. 如图: 什么是堆排序(Heapsort)? 利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种.可以利用数组的特点快速定位指定索引的元素. 现在给我们一个无序数组,我们将其从小到大排序,使用堆排序的实现步骤和思想如下: 1.让这个数组变成一个大根堆 2.将最后一

  • Java计算器核心算法代码实现

    在进行一个表达式的计算时,先将表达式分割成数字和字符串然后利用出入栈将分割后的表达式进行中缀转后缀,再将后缀表达式进行计算得到结果(思想在上一篇写过)现在贴下Java语言的代码实现.(学习Java时间不长所以可能会有很多不足的地方,我会改进也欢迎大神可以给我一些意见和建议~谢谢啦) 我将这部分分成三个方法完成功能,并在getResult方法调用(getResult方法被主方法调用) private String getResult(String str) { //分割 String[] Str

  • JavaScript键盘事件常见用法实例分析

    本文实例讲述了JavaScript键盘事件常见用法.分享给大家供大家参考,具体如下: JavaScript 键盘事件有以下3种 keydown 键盘按键按下(如果按着不放,会持续触发该事件),可以捕获组合键. keypress 键盘非功能按键按下(在keydown之后触发,如果按着不放会持续触发该事件),只能捕获单个键. keyup 键盘按键弹起,可以捕获组合键. 全局事件对象event event.ctrlKey 功能键"ctrl"键是否按下. event.altKey 功能键&qu

  • JavaScript实现动态添加、移除元素或属性的方法分析

    本文实例讲述了JavaScript实现动态添加.移除元素或属性的方法.分享给大家供大家参考,具体如下: JavaScript 动态添加.移除元素 appendChild(newnode) 向节点的子节点列表的末尾添加新的子节点. insertBefore(newnode, existingnode) 在已有子节点之前插入新的子节点. removeChild(node) 删除元素的某个指定的子节点,并以 Node 对象返回被删除的节点,如果节点不存在则返回 null. innerHTML 属性设置

  • 原生javascript实现连连看游戏

    本文实例为大家分享了js实现连连看游戏的具体代码,供大家参考,具体内容如下 <!DOCTYPE html> <html> <head> <title>连连看</title> <meta charset="gbk"> <style type="text/css"> body { text-align: center; } .text {text-align: center; margi

随机推荐