先序遍历
第一次遇到该结点时输出
public void preOrder(TreeNode root){
if(root==null) return;
TreeNode p = root;
Stack<TreeNode> stack = new Stack<>();
while(!stack.isEmpty()||p!=null){
//对每一棵树而言,都一直向左下走,直到走到最左下的叶子结点
while(p!=null){
System.out.print(p.val+" "); //先序遍历是第一次遇到该结点的时候输出,即结点入栈
stack.push(p);
p = p.left;
}
if(!stack.isEmpty()){
p = stack.pop();
p = p.right; //进入最左下叶子结点的右孩子
}
//进入新一轮循环
}
}
中序遍历
第二次遇到该结点时输出
public void inOrder(TreeNode root){
if(root==null) return;
TreeNode p = root;
Stack<TreeNode> stack = new Stack<>();
while(!stack.isEmpty()||p!=null){
//对每一棵树而言,都一直向左下走,直到走到最左下的叶子结点
while(p!=null){
stack.push(p);
p = p.left;
}
if(!stack.isEmpty()){
p = stack.pop();
System.out.print(p.val+" "); //中序遍历是第二次遇到该结点的时候输出,即结点出栈
p = p.right; //进入最左下叶子结点的右孩子
}
//进入新一轮循环
}
}
后序遍历
第三次遇到该结点时输出
(alreadyPushed标记可省略)
public static void postOrder(TreeNode root){
if(root==null) return;
TreeNode p = root,lastVisted = root;
Stack<TreeNode> stack = new Stack<>();
//boolean alreadyPushed = false;
while(!stack.isEmpty()||p!=null){
//根据alreadyPushed判断右子树是否已经被压入栈过
//if(!alreadyPushed){
while(p!=null){ //未被压入栈过,则依次入栈
stack.push(p);
p = p.left;
}
//}
//后序遍历是第三次遇到该结点的时候输出,只有两种情况:
//①该结点的右孩子为空(这里也看作第二次遇到后,先去了空的右孩子,再回头,所以是第三次遇到)
//②该结点的右孩子已经访问过了(还可以肯定的是,被访问过的右孩子一定就是上一次访问的结点)
if(!stack.isEmpty()){
p = stack.pop(); //可能是第二或第三次遇到,要做进一步判断
if(p.right==null||p.right==lastVisted){ //第三次遇到:①右孩子为空||②上一次访问的结点是它的右孩子
System.out.print(p.val + " ");
lastVisted = p; //更新上一个访问的结点
p = null; //必须设空,否则最外层循环条件p!=null可能永远无法满足
//alreadyPushed = true; //右孩子已经访问过了(已被压入栈过),不需要再进入右孩子
}else{
stack.push(p); //说明右孩子还没访问过,将该结点再次入栈
p = p.right; //右孩子还没有访问过(未被压入栈过),先进入右孩子
//alreadyPushed = false;
}
}
}
}