二叉树的非递归前序,中序,后序遍历的Java实现
二叉树是经常使用的树形结构,其遍历也是经常用到的。利用递归可以很简单的写出前中后序的遍历。本文采用非递归方式进行遍历,先上代码,解释在后面。
此代码是一个抽象类,节点的操作在具体类中实现。前序遍历有两种实现,一种是标准实现(与中序遍历很相似),一种是我自己的实现。
package travelTree;
import java.util.Stack;
public abstract class TreeTravelwithNoRecursive {
public void myfirstOrder(TreeNode root) {
if(root == null) return;
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.push(root);
while(!stack.isEmpty()) {
TreeNode node = stack.pop();
doWithNode(node);
if(node.right != null) {
stack.push(node.right);
}
if(node.left != null) {
stack.push(node.left);
}
}
}
public void firstOrder(TreeNode root) {
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode node = root;
while( node!=null || !stack.empty() ) {
while(node!= null) {
stack.push(node);
doWithNode(node);
node = node.left;
}
if( !stack.isEmpty() ) {
node = stack.pop();
node = node.right;
}
}
}
public void middleOrder(TreeNode root) {
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode node = root;
while( node!=null || !stack.empty() ) {
while(node!= null) {
stack.push(node);
node = node.left;
}
if( !stack.isEmpty() ) {
node = stack.pop();
doWithNode(node);
node = node.right;
}
}
}
public void myafterOrder(TreeNode root) {
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode node = root;
TreeNode pre = null;
if(root == null)return;
stack.push(root);
while( !stack.empty() ) {
node = stack.peek();
if((node.left == null && node.right == null)
||(pre!=null && (pre == node.left || pre == node.right))) {
node = stack.pop();
doWithNode(node);
pre = node;
}
else {
if(node.right != null) {
stack.push(node.right);
}
if(node.left != null) {
stack.push(node.left);
}
}
}
}
public abstract void doWithNode(TreeNode node);
public static void main(String[] args) {
TreeNode root = new TreeNode(1);
TreeNode n2 = new TreeNode(2);
TreeNode n3 = new TreeNode(3);
TreeNode n4 = new TreeNode(4);
TreeNode n5 = new TreeNode(5);
TreeNode n6 = new TreeNode(6);
TreeNode n7 = new TreeNode(7);
TreeNode n8 = new TreeNode(8);
root.left = n2;
root.right = n3;
n2.left = n4;
n2.right = n5;
n3.left = n6;
n4.left = n7;
n5.right = n8;
TreeTravelwithNoRecursive travel = new TreeTravelwithNoRecursive() {
@Override
public void doWithNode(TreeNode node) {
System.out.println(node.val);
}
};
// travel.myfirstOrder(root);
// travel.firstOrder(root);
// travel.middleOrder(root);
travel.myafterOrder(root);
}
}
package travelTree;
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
三种遍历的规则:
- 前序遍历:先访问根节点,接着访问左子树,然后是右子树
- 中序遍历:先访问左子树,接着访问根节点,最后是右子树
- 后序遍历:先访问左子树,接着访问右子树,最后是根节点
非递归实现:
前序遍历
方法一:
先将根节点入栈
stack.push(root);
接着是一个循环直到栈为空,循环中做的事情则是,出栈并处理节点;接着若右子树不为空,则入栈;再接着左子树不为空,则入栈
while(!stack.isEmpty()) {
TreeNode node = stack.pop();
doWithNode(node);
if(node.right != null) {
stack.push(node.right);
}
if(node.left != null) {
stack.push(node.left);
}
}
方法二:
利用一个游标节点node标记当前的位置(初始指向root),当node不为空,或者栈不为空时循环:若node不为空,则将node入栈,并处理节点,同时让node指向其左子树,循环直至node为空;接着若栈不为空,则出栈一个元素,并将node指向其右子树。
while( node!=null || !stack.empty() ) {
while(node!= null) {
stack.push(node);
doWithNode(node);
node = node.left;
}
if( !stack.isEmpty() ) {
node = stack.pop();
node = node.right;
}
}
中序遍历
与前序遍历的方法二流程一样,只是处理节点的位置由入栈时改为出栈时
利用一个游标节点node标记当前的位置(初始指向root),当node不为空,或者栈不为空时循环:若node不为空,则将node入栈,同时让node指向其左子树,循环直至node为空;接着若栈不为空,则出栈一个元素,并处理节点,并将node指向其右子树。
while( node!=null || !stack.empty() ) {
while(node!= null) {
stack.push(node);
node = node.left;
}
if( !stack.isEmpty() ) {
node = stack.pop();
doWithNode(node);
node = node.right;
}
}
后序遍历
后序遍历其实也有两种实现方式,我这里是易理解的一种
该方法的要点是
- 右子树比左子树先入栈,保证每次节点的左子树在右子树之前遍历到
- 要遍历该节点,则必须保证其左右子树均已遍历过了。如何判断其左右子树均已遍历呢?设置一个pre的节点,记录上次遍历的节点,若上次遍历的是其左子树(无右子树的情况)或右子树,则可以遍历该节点,否则先遍历其左右子树
node = stack.peek();
if((node.left == null && node.right == null)
||(pre!=null && (pre == node.left || pre == node.right))) {
node = stack.pop();
doWithNode(node);
pre = node;
}
else {
if(node.right != null) {
stack.push(node.right);
}
if(node.left != null) {
stack.push(node.left);
}
}