二叉樹的前序、中序、後序遍歷
每個節點會被經過3次,前序、中序、後序的區別在於:在哪一次經過該節點時對其進行訪問。
2. 遞迴實現
traverseRecursive(BiTrNode<T>* node): basecase: if(node == nullptr) return; general: 1 print(node->data); 2 traverseRecursive(node->lchild); 3 traverseRecursive(node->rchild); 前序:123 中序:213 後序:132
3. 非遞迴實現
首先需要明確的是,所謂的遞迴實現其實是在模擬遞迴棧,所以使用的基本資料結構是stack。
這裡如何設計節點的入棧策略是關鍵,因為執行的時候無非就是依次從stack中取出棧頂元素進行處理,所以stack中節點的入棧順序就是訪問順序。
那既然我們已經知道了,每個節點都會被經過3次,所謂前序、中序、後序的區別無非就是第幾次經過時對其進行操作(這裡我們把對節點的操作簡化成print node->data)。
初步想法
給每一個節點增加一個欄位(lookTimes)表示這是第幾次經過該節點。
模擬一下3次經過的入棧順序:①第一次經過該節點 -> ②遍歷該節點的左孩子 -> ③回到該節點(第二次經過)-> ④遍歷該節點的右孩子 -> ⑤回到該節點(第三次經過)。
init:所有節點的lookTimes都設為0,將根節點入棧。
從stack中取出棧頂元素表示經過一次該節點(lookTimes++),然後看lookTimes進行相應的操作:
- lookTimes = 1:第一次訪問該節點,接下來要訪問該節點的左子樹,然後再回到該節點,再所以應該依次:push(左孩子),push(自己)
- lookTimes = 2:第二次訪問該節點,接下來要訪問該節點的右子樹,然後再回到該節點,所以應該依次:push(右孩子),push(自己)
- lookTimes = 3:第三次訪問該節點,此時對該節點的3次訪問就結束了,所以就沒有什麼額外操作等著取下一個節點就行了。
模擬好3次經過之後,要實現前序/中序/後序遍歷只需在每一次push前print(node->data)就可以了。比如,如果是前序遍歷就在p->lookTimes == 1的if語句塊的第一行加上print(node->data)就可以了。
偽碼如下:
//version1.0 while(stack is not empty){ p = stack.pop(); (p->lookTimes)++; if(p->lookTimes == 1){ //print(p->data); 前序遍歷 stack.push(p); if(p->lchild) stack.push(p->lchild); }else if(p->lookTimes == 2){ //print(p->data); 中序遍歷 stack.push(p); if(p->lchild) stack.push(p->rchild); }else if(p->lookTimes == 3){ //print(p->data); 後序遍歷 } }
優化版本
上面是一種比較通用的想法,但是在考慮到具體的某一種遍歷方式時,比如前序遍歷:既然在lookTimes == 1時就已經對該節點進行訪問了,那麼後面的兩次回到該節點顯然是沒有必要的。
因此根據每一種遍歷方式的特點進行優化:
1. 前序
入棧順序:①第一次經過該節點 -> ②遍歷該節點的左孩子 -> ③遍歷該節點的右孩子。而且這樣的話每個節點只會經過一次,lookTimes欄位也就沒有必要了。
//前序遍歷_version2.0:without lookTimes while(stack is not empty){ p = stack.pop(); print(p->data); if(p->lchild) stack.push(p->lchild); if(p->rchild) stack.push(p->rchild); }
2. 中序
入棧順序:①第一次經過該節點 -> ②遍歷該節點的左孩子 -> ③回到該節點(第二次經過)-> ④遍歷該節點的右孩子。
//中序遍歷_version2.0 while(stack is not empty){ p = stack.pop(); (p->lookTimes)++; if(p->lookTimes == 1){ stack.push(p); if(p->lchild) stack.push(p->lchild); }else if(p->lookTimes == 2){ print(p->data); if(p->lchild) stack.push(p->rchild); } }
3. 後序
後序因為在第三次經過時才會訪問,所以不能再優化了。