1. 程式人生 > >後續遍歷--遞迴與非遞迴(java版)

後續遍歷--遞迴與非遞迴(java版)

先訪問左右孩子,再訪問根節點。同樣還是採用棧的形式,但是問題是,先訪問左孩子出棧,根節點不能刪除,再訪問右孩子出棧,最後訪問根節點出棧。我們的思路是這樣的:

對於某個節點p:

   1)將p壓入棧中,並將p所有的左孩子,全部壓入棧中:

    while(stk.isEmpty() == false) {            
            if(p!=null && p.left!=null) {
                stk.add(p.left);
                p = p.left;
            }else
               .....
    }

      2)將棧頂元素彈出,訪問。如果沒有右孩子,棧頂元素繼續出棧。如果有右孩子,將右孩子壓入棧中,pVisit標記其右孩子,表示已經訪問過了。當右孩子訪問過之後,回到 1 節點(如下圖),p.right = pVisit,表示右節點已經訪問過了。

4)直到stack.isEmpty == true,則遍歷結束;

 

需要一個輔助節點pRight,它專門指向已經訪問過的右節點,例如(結合程式看):

import java.util.Stack;

class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}
public class Main {
	//遞迴求解
	public static void postOrder(TreeNode root) {
		if(root == null)return;
		postOrder(root.left);
		postOrder(root.right);
		System.out.print(root.val +" ");
	}
	//非遞迴求解
	public static void postOrder2(TreeNode root) {
		if(root == null)return;
		TreeNode p = root;
		TreeNode pVisit = null;
		Stack<TreeNode> stk = new Stack<TreeNode>();
		stk.add(p);
		
		while(stk.isEmpty() == false) {
			//只要你有左孩子,就將左孩子壓入棧中
			if(p!=null && p.left!=null) {
				stk.add(p.left);
				p = p.left;
			}else {
				p = stk.peek();//棧頂元素,先出棧,可能還有右孩子
				if(p.right==null  || p.right==pVisit) {//如果沒有右孩子或右孩子已經訪問過了,出棧
					System.out.print(p.val+" ");
					pVisit = p;//這個很重要,考慮一下只有右孩子的樹,得不斷的回溯
					p = null;//沒有新節點加入,繼續進行出棧操作
					stk.pop();
				}else {//如果有右孩子,右孩子入棧
					pVisit = p.right;
					stk.add(p.right);
					p = p.right;
				}
			}
		}
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TreeNode root = new TreeNode(1);
		 root.left = new TreeNode(2);
		 root.right = new TreeNode(3);
		 
		 root.left.left = new TreeNode(4);
		 root.left.right = new TreeNode(5);
		 
		 root.right.left = new TreeNode(6);
		 root.right.right = new TreeNode(7);
		
		 postOrder2(root);
	}

}