- 中序非递归遍历
自己走一遍就可以理解,但是总是记不住
//边界条件判断
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