二叉樹遍歷演算法之三:後序遍歷
後續遍歷的遞迴實現
後續遍歷指的是先訪問節點的左右孩子,最後訪問節點本身。所以使用後序遍歷得到的結果的最後一個節點就是根節點。採用後續遍歷的具體步驟如下:
- 先訪問根節點,如果有左孩子,進入第二步;如果有右孩子,進入第三步
- 對左孩子繼續判斷其是否有左孩子,直到某節點的左孩子為空,設為cur節點
- 對右孩子繼續判其是否有左孩子,直到某個節點的左孩子為空,設為curR節點
- cur節點訪問之後,訪問其雙親節點的右孩子,如果其雙親節點的右孩子不為空的話。接著訪問cur節點的雙親節點,設為curP節點。
- cur節點的雙親節點訪問結束之後,繼續訪問curP節點的右孩子(如果不為空的話),接著訪問curP節點本身
- 重複以上過程直到根節點
- 繼續訪問根節點的右孩子,回到第三步,訪問curR節點之後,訪問其雙親節點的右孩子(如果有的話)
- 最後訪問curR的雙親節點本身
重複以上過程直到訪問根節點,訪問結束
比如下面的二叉樹後序遍歷的訪問結果是:24,15,5,7,6,30,9,28,11,10,8
那麼使用遞迴實現的程式碼如下:
public void postOrderTraverse(TreeNode node){
if(node == null) return;
postOrderTraverse(node.left);
postOrderTraverse(node.right);
System.out .print(node.val + " ");
}
後續遍歷的非遞迴實現
後續遍歷的非遞迴實現與前面的前序遍歷和中序遍歷的方式有一些不同,由於根節點是最後訪問的,在訪問的時候建立一個棧儲存遍歷的結果還不夠,所以需要使用一個輔助棧來記住每個訪問節點的雙親節點。先看程式碼:
public void postOrderTraverse2(TreeNode node){
Stack<TreeNode> s1 = new Stack<TreeNode>();
//建立棧s2的目的在於記住每個訪問的節點
Stack< Integer> s2 = new Stack<Integer>();
//如果棧s2的棧頂是1,標識當前訪問的節點
Integer i = new Integer(1);
while(node != null || !s1.isEmpty()){
while(node != null){
s1.push(node);
s2.push(0);
node = node.left;
}
//這個循壞的目的是對棧s2棧頂為1時對應的棧s1的棧頂元素進行訪問
while(!s1.isEmpty() && s2.peek().equals(i)){
s2.pop();
System.out.print(s1.pop().val + " ");
}
//訪問左子樹到頭後,就可以訪問其右孩子了
if(!s1.isEmpty()){
s2.pop();
s2.push(i);
node = s1.peek();
node = node.right;
}
}
}
下面具體說明具體的執行過程:
根節點8不為空,放入棧s1中,同時往棧s2中放入0,由於8的左孩子是6不為空,所以繼續放入棧s1中,同時往棧s2中放入0,直到節點15,由於其左孩子為空,所以第一個循壞結束,現在兩個棧的情況是這樣的:
由於棧s2的棧頂元素是0,不符合,所以第二個迴圈不執行,直接跳到if判斷語句,把棧s2的棧頂元素彈出,同時壓入元素1,並且取出棧s1的棧頂元素,也就是15,訪問15的右孩子24
第二次迴圈開始,仍然滿足第一個迴圈,於是把24放入棧s1,同時在棧s2壓入一個0,由於24是葉子節點,所以第一個迴圈結束。前兩次操作執行完畢後,兩個棧的情況是這樣的:
由於棧s2的棧頂元素仍然是0,所以第二個迴圈繼續跳過,到if判斷語句。彈出棧s2的棧頂元素0,同時壓入元素1。並讓當前節點指向24的右孩子
第三次迴圈開始,由於24沒有右孩子,所以將跳過第一個迴圈,現在棧s2的棧頂元素是1,所以彈出棧s1的棧頂元素和棧s2的棧頂元素1,並輸出24。執行以上兩步之後,兩個棧時這樣的:
繼續執行第二個while迴圈(因為棧s2的棧頂仍然是1),彈出1,彈出15,接著輸出15。迴圈結束
進入if判斷語句,彈出棧頂元素0,壓入元素1,並讓當前節點指向5的右孩子。這時兩個棧時的情況如下:
第四次迴圈開始,由於5沒有右孩子,第一個迴圈跳過,這時棧s2的棧頂是1,所以將彈出1,彈出5,接著輸出5。這時兩個棧是這樣的:
9. 繼續執行if語句,棧s2彈出0,壓入1,並讓當前節點指向6的右孩子,也就是節點7
10. 開始第五次迴圈,首先第一個迴圈,將7壓入棧s1中,同時棧s2壓入0。棧s2的棧頂元素是0,不滿足第二個迴圈的條件,執行if語句。此時兩個棧的情況如下:
11. 彈出0,壓入1,並讓當前節點指向7的右孩子
12. 開始第六次迴圈,7沒有右孩子,直接執行第二個迴圈,將執行兩次,棧s1分別彈出7和6,並輸出依次輸出7和6,同時棧s2分別彈出兩個1,最後棧的情況是這樣的:
13. 執行if語句,彈出0,壓入1,並讓當前節點指向8的右孩子,也就是10。後面的過程與上面類似,看圖就好了