二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的。对于二叉树,有前序、中序以及后序三种遍历方法。因为树的定义本身就是递归定义,因此采用递归的方法去实现树的三种遍历不仅容易理解而且代码很简洁。而对于树的遍历若采用非递归的方法,就要采用栈去模拟实现。在三种遍历中,前序和中序遍历的非递归算法都很容易实现,非递归后序遍历实现起来相对来说要难一点。

节点分布如下:

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack; /**
* @author 李文浩
* @version 2017/7/30.
*/
public class BinaryTree {
/**
* 节点定义
*/
static class TreeNode {
int val;
TreeNode left;
TreeNode right; TreeNode(int x) {
val = x;
}
}
/**
* 高度,左右子树中的较大值
*
* @param node
* @return
*/
public static int height(TreeNode node) {
if (node == null) {
return 0;
}
int leftHeight = height(node.left);
int rightHeight = height(node.right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
} /**
* 层序遍历一颗二叉树,用广度优先搜索的思想,使用一个队列来按照层的顺序存放节点
* 先将根节点入队列,只要队列不为空,然后出队列,并访问,接着讲访问节点的左右子树依次入队列
*
* @param node
*/
public static void levelTraversal(TreeNode node) {
if (node == null)
return;
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(node);
TreeNode treeNode;
while (!queue.isEmpty()) {
treeNode = queue.poll();
System.out.print(treeNode.val + " ");
if (treeNode.left != null) {
queue.offer(treeNode.left);
}
if (treeNode.right != null) {
queue.offer(treeNode.right);
}
}
} /**
* 先序递归
*
* @param treeNode
*/
public static void preOrder(TreeNode treeNode) {
if (treeNode != null) {
System.out.print(treeNode.val + " ");
preOrder(treeNode.left);
preOrder(treeNode.right);
}
} /**
* 中序递归
*
* @param treeNode
*/
public static void inOrder(TreeNode treeNode) {
if (treeNode != null) {
inOrder(treeNode.left);
System.out.print(treeNode.val + " ");
inOrder(treeNode.right);
}
} /**
* 后序递归
*
* @param treeNode
*/
public static void postOrder(TreeNode treeNode) {
if (treeNode != null) {
postOrder(treeNode.left);
postOrder(treeNode.right);
System.out.print(treeNode.val + " ");
}
} /**
* 先序非递归:
* 这种实现类似于图的深度优先遍历(DFS)。
* 维护一个栈,将根节点入栈,然后只要栈不为空,出栈并访问,
* 接着依次将访问节点的右节点、左节点入栈。
* 这种方式应该是对先序遍历的一种特殊实现(看上去简单明了),
* 但是不具备很好的扩展性,在中序和后序方式中不适用
*
* @param root
*/
public static void preOrderStack(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode treeNode = stack.pop();
System.out.print(treeNode.val + " ");
if (treeNode.right != null) {
stack.push(treeNode.right);
}
if (treeNode.left != null) {
stack.push(treeNode.left);
}
}
} /**
* 先序非递归2:
* 利用栈模拟递归过程实现循环先序遍历二叉树。
* 这种方式具备扩展性,它模拟递归的过程,将左子树点不断的压入栈,直到null,
* 然后处理栈顶节点的右子树。
*
* @param root
*/
public static void preOrderStack2(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode treeNode = root;
while (treeNode != null || !stack.isEmpty()) {
//将左子树点不断的压入栈
while (treeNode != null) {
//先访问再入栈
System.out.print(treeNode.val + " ");
stack.push(treeNode);
treeNode = treeNode.left;
}
//出栈并处理右子树
if (!stack.isEmpty()) {
treeNode = stack.pop();
treeNode = treeNode.right;
} } } /**
* 中序非递归:
* 利用栈模拟递归过程实现循环中序遍历二叉树。
* 思想和上面的先序非递归2相同,
* 只是访问的时间是在左子树都处理完直到null的时候出栈并访问。
*
* @param treeNode
*/
public static void inOrderStack(TreeNode treeNode) {
Stack<TreeNode> stack = new Stack<>();
while (treeNode != null || !stack.isEmpty()) {
while (treeNode != null) {
stack.push(treeNode);
treeNode = treeNode.left;
}
//左子树进栈完毕
if (!stack.isEmpty()) {
treeNode = stack.pop();
System.out.print(treeNode.val + " ");
treeNode = treeNode.right;
}
}
} public static class TagNode {
TreeNode treeNode;
boolean isFirst;
} /**
* 后序非递归:
* 后序遍历不同于先序和中序,它是要先处理完左右子树,
* 然后再处理根(回溯)。
* <p>
* <p>
* 对于任一结点P,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,
* 此时该结点出现在栈顶,但是此时不能将其出栈并访问,因此其右孩子还为被访问。
* 所以接下来按照相同的规则对其右子树进行相同的处理,当访问完其右孩子时,该结点又出现在栈顶,
* 此时可以将其出栈并访问。这样就保证了正确的访问顺序。
* 可以看出,在这个过程中,每个结点都两次出现在栈顶,只有在第二次出现在栈顶时,才能访问它。
* 因此需要多设置一个变量标识该结点是否是第一次出现在栈顶,这里是在树结构里面加一个标记,然后合成一个新的TagNode。
*
* @param treeNode
*/
public static void postOrderStack(TreeNode treeNode) {
Stack<TagNode> stack = new Stack<>();
TagNode tagNode;
while (treeNode != null || !stack.isEmpty()) {
//沿左子树一直往下搜索,直至出现没有左子树的结点
while (treeNode != null) {
tagNode = new TagNode();
tagNode.treeNode = treeNode;
tagNode.isFirst = true;
stack.push(tagNode);
treeNode = treeNode.left;
} if (!stack.isEmpty()) {
tagNode = stack.pop();
//表示是第一次出现在栈顶
if (tagNode.isFirst == true) {
tagNode.isFirst = false;
stack.push(tagNode);
treeNode = tagNode.treeNode.right;
} else {
//第二次出现在栈顶
System.out.print(tagNode.treeNode.val + " ");
treeNode = null;
}
}
}
} /**
* 后序非递归2:
* 要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;
* 或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。
* 若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子前面被访问,
* 左孩子和右孩子都在根结点前面被访问。
*
* @param treeNode
*/
public static void postOrderStack2(TreeNode treeNode) {
Stack<TreeNode> stack = new Stack<>();
TreeNode currentTreeNode;
TreeNode preTreeNode = null;
stack.push(treeNode); while (!stack.isEmpty()) {
currentTreeNode = stack.peek();
//如果当前结点没有孩子结点或者孩子节点都已被访问过
if ((currentTreeNode.left == null && currentTreeNode.right == null) ||
(preTreeNode != null && (preTreeNode == currentTreeNode.left || preTreeNode == currentTreeNode.right))) {
System.out.print(currentTreeNode.val + " ");
stack.pop();
preTreeNode = currentTreeNode;
} else {
if (currentTreeNode.right != null) {
stack.push(currentTreeNode.right);
}
if (currentTreeNode.left != null) {
stack.push(currentTreeNode.left);
}
}
}
}
}

参考文档

  1. 二叉树的非递归遍历
  2. JAVA下实现二叉树的先序、中序、后序、层序遍历(递归和循环)

Java实现二叉树的先序、中序、后序、层序遍历(递归和非递归)的更多相关文章

  1. LeetCode:二叉树的前、中、后序遍历

    描述: ------------------------------------------------------- 前序遍历: Given a binary tree, return the pr ...

  2. leetcode(144,94,145,102)中迭代版的二叉树的前、中、后、层级遍历

    //前序遍历class Solution{ public: vector<int> preorderTraversal(TreeNode *root){ vector<int> ...

  3. Java实现二叉树的前序、中序、后序遍历(非递归方法)

      在上一篇博客中,实现了Java中二叉树的三种遍历方式的递归实现,接下来,在此实现Java中非递归实现二叉树的前序.中序.后序遍历,在非递归实现中,借助了栈来帮助实现遍历.前序和中序比较类似,也简单 ...

  4. Java实现二叉树的前序、中序、后序、层序遍历(非递归方法)

      在上一篇博客中,实现了Java中二叉树的四种遍历方式的递归实现,接下来,在此实现Java中非递归实现二叉树的前序.中序.后序.层序遍历,在非递归实现中,借助了栈来帮助实现遍历.前序和中序比较类似, ...

  5. [Java]算术表达式求值之二(中序表达式转后序表达式方案,支持小数)

    Inlet类,入口类,这个类的主要用途是验证用户输入的算术表达式: package com.hy; import java.io.BufferedReader; import java.io.IOEx ...

  6. [Java]算术表达式求值之一(中序表达式转后序表达式方案)

    第二版请见:https://www.cnblogs.com/xiandedanteng/p/11451359.html 入口类,这个类的主要用途是粗筛用户输入的算术表达式: package com.h ...

  7. 分别求二叉树前、中、后序的第k个节点

    一.求二叉树的前序遍历中的第k个节点 //求先序遍历中的第k个节点的值 ; elemType preNode(BTNode *root,int k){ if(root==NULL) return ' ...

  8. 已知树的前序、中序,求后序的java实现&已知树的后序、中序,求前序的java实现

    public class Order { int findPosInInOrder(String str,String in,int position){ char c = str.charAt(po ...

  9. DS Tree 已知先序、中序 => 建树 => 求后序

    参考:二叉树--前序和中序得到后序 思路历程: 在最初敲的时候,经常会弄混preorder和midorder的元素位置.大体的思路就是在preorder中找到根节点(根节点在序列的左边),然后在mid ...

随机推荐

  1. “Hello World!”团队——Alpha发布用户使用报告

    博客内容: 1.用户体验报告表 2.用户评论截图 3.总结 一.用户体验报告表 用户使用报告 用户序号 用户姓名(化名) 性别 用户职业 使用频次 用户评论 1 小董 女 文学在读硕士 5 1.游戏界 ...

  2. 计算器软件实现系列(七)WPF+SQL+策略模式

    一  整体概述 本次设计主要是在WPF的页面中实现的,属于表现层的更换,数据库部分用的还是数据库的封装,其中引用了策略模式 二  设计思路 1 在出题页面,进行试题的编辑,在编辑后会自动保存到数据库中 ...

  3. Java之I/O流(第1部分)

    Java 中的I/O流: 1. 输入/输出流原理: 如下图所示:在 java 程序中,对于数据的输入/输出操作以“流”(Stream)的方式进行:J2SDK 提供了各种各样的“流”类,用来获取不同种类 ...

  4. <Effective C++>读书摘要--Introduction

    Introduction 1.Learning the fundamentals of a programming language is one thing; learning how to des ...

  5. mini2440 Nor Flash工作原理分析

    我的mini2440上是只接了一块Nor Flash,型号是S29AL016M90TAI02,这是一块2M Byte,16位宽度的Nor Flash,用于引导扇区的闪存.原理图里面关键的引脚是: 地址 ...

  6. phpmyadmin打开空白

    本地phpstudy环境,打开 phpmyadmin,登陆之后,显示空白页面. 解决办法:切换为 低版本的php版本,正常登陆.

  7. jenkins部署springboot多项目

    war包的部署问题不大,这里记录jar包的部署过程: 1:jar包的体积过大问题 pom.xml参考以下配置(依赖包会分离到target/lib/,jar包体积由几十M缩小到几k) <build ...

  8. jira & analytics

    jira & analytics jira 代码有毒呀 http://jira.xgqfrms.xyz:8888/rest/analytics/1.0/publish/bulk { resou ...

  9. VBA练习-打开文件,添加选中项,生成新表

    学习VBA,正好给财务制作一个小工具: Sub 打开人员信息表() Dim wb As Workbook, c As Integer Set wb = Workbooks.Open(, True) c ...

  10. Go语言【第十二篇】:Go数据结构之:切片(Slice)、范围(Range)、集合(Map)

    Go语言切片(Slice) Go语言切片是对数组的抽象,Go数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数 ...