您的位置:首页 > 编程语言 > Java开发

二叉树先序,中序,后序的非递归遍历(附java实现)

2017-04-23 11:35 357 查看
中序非递归遍历

自己走一遍就可以理解,但是总是记不住

//边界条件判断
if(root==null)
return;
Stack stack=null;
p=root;//这样设置,而非将root入栈,可以代表每个完整树的开始

while(p非空||stack非空){
//沿着p的左指针非空进栈走到最左,最左边可能有两种情况,最左子树无左右子树和最左子树只有右子树
while(p非空){
stack.push(p);
p=p.lchild;
}
//我理解此时,栈必不为空;所以无需判断栈空
stack.pop(p);
visit(p);//出栈visit;
p=p.rchild;//再从p的右子树循环走到最左
}


先序非递归遍历

if(root==null)
return;
Stack stack=null;
p=root;//循环时与初始条件无关,易于接下来的分析

while(p非空||stack非空){
//沿着p的左指针非空进栈走到最左
while(p非空){
stack.push(p);
visit(p);//先序遍历,先访问结点,存入栈中是为了查找右子树
p=p.lchild;
}
//同理,栈必不为空;所以直接弹出
stack.pop(p);
p=p.rchild;//右子树无论空或非空直接下一个while就行
}


对比先序和中序的非递归遍历,差别只是在visit(p)函数的调用位置,遍历规则大同小异,:

均是先走到二叉树的最左,先序需要边走边visit。pop栈只是要走右子树;中序需要保证左树遍历完,才可以pop栈访问,再走右子树;

后序非递归遍历

后续非递归时,一个根节点能够被访问的前提是:无右子树或右子树已被访问过。所以以右子树为条件,在元素出栈后,访问元素前,需要判断lastVisit是否是该节点的右子树,若是,则执行访问;若不是说明没有访问该节点的右子树,此节点目前不能访问,因此将其再次入栈,继续此节点的右子树循环

//边界条件判断
if(root==null)
return;
Stack stack = null;
lastVisit = null;//记录上次访问的结点
p = root;

while(p非空||stack非空){
//后序遍历也需走到最左
while(p非空){
stack.push(p);
p=p.lchild;
}
//栈必不为空,直接弹出
stack.pop(p);
//如果此节点的右子树为空,或者上次访问的结点 是此节点的右子树,则表明 现在!  可以访问 此节点!
if(p.rchild == null || lastVisit == p.rchild){
visit(p);
lastVisit=p;
p=null;//此处必须设null,否则循环
}
else{
stack.push(p);//对于刚才的出栈,现已知是错误的选择,所以再入栈
p=p.rchild;//再从p的右子树开始新的循环
}
}


java实现的三种遍历

import java.util.ArrayDeque;
import java.util.Deque;

class Node {
int item;
Node left;
Node right;

Node(Node left, int element, Node right) {
this.item = element;
this.left = left;
this.right = right;
}
}

public class Travesal {

public static void preOrder(Node root) {
Node p = root;
Deque<Node> stack = new ArrayDeque<Node>();
if (p != null) {
while (!stack.isEmpty() || p != null) {
while (p != null) {
System.out.println(p.item);
stack.offerFirst(p);// offerFirst prefer stack.addFirst(p);
p = p.left;
}
p = stack.pollFirst();// pollLast prefer removeLast();
p = p.right;
}
}

}
public static void inOrder(Node root) {
Node p = root;
Deque<Node> stack = new ArrayDeque<Node>();
if (p != null) {
while (!stack.isEmpty() || p != null) {
while (p != null) {
stack.offerFirst(p);// offerFirst prefer stack.addFirst(p);
p = p.left;
}
p = stack.pollFirst();// pollLast prefer removeLast();
System.out.println(p.item);
p = p.right;
}
}

}

public static void postOrder(Node root) {
Node p = root;
Node lastVisit=null;
Deque<Node> stack = new ArrayDeque<Node>();//基于循环队列的双端队列
if (p != null) {
while (!stack.isEmpty() || p != null) {
while (p != null) {
stack.offerFirst(p);// offerFirst prefer than stack.addFirst(p);
p = p.left;
}
p = stack.pollFirst();// pollLast prefer than removeLast();
if(lastVisit==p.right||p.right==null){
System.out.println(p.item);
lastVisit=p;
p=null;//一定要置空,否则死循环
}else{
stack.offerFirst(p);//对于刚才的出栈,现已知是错误的选择,所以再入栈
p = p.right;
}
}
}

}

public static void main(String[] args) {

Node node9 = new Node(null, 9, null);
Node node7 = new Node(node9, 7, null);

Node node12 = new Node(null, 12, null);
Node node13 = new Node(null, 13, null);

Node node8 = new Node(node12, 8, node13);
Node node3 = new Node(node7, 3, node8);
Node node4 = new Node(null, 4, null);
Node node1 = new Node(node3, 1, node4);

//preOrder(node1);inOrder(node1);
postOrder(node1);
}

}


参考:

三种遍历方法详细介绍

http://blog.csdn.net/zhangxiangdavaid/article/details/37115355
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  遍历
相关文章推荐