二叉樹:後序,遞迴和非遞迴,應用(求祖先問題)
阿新 • • 發佈:2019-01-31
1 宣告
2 後序
a 遞迴
void PostOrder(BiTree T) {
if (T) {
PostOrder(T->lChild);
PostOrder(T->rChild);
visit(T);
}
}
b 非遞迴
i)由於是後序遍歷,必須先訪問左右,再訪問根,只有通過設定棧儲存之前訪問過的節點,這樣才能在訪問完右子樹之後回到根節點。
ii)這樣,就有兩種情況訪問到根節點。一種是第一次訪問,另一種是已經訪問過子樹後回到根。 為了區分兩種情況,設定一個指標用於標識訪問過的節點。
void PostOrder_2(BiTree T) { if (!T) { Error(); return; } std::stack<BiTNode*> st; BiTNode *p = T; BiTNode *flag = nullptr; //標識被訪問過的節點 while (p || !st.empty()) { if (p) { st.push(p); p = p->lChild; } else { p = st.top(); //取當前棧頂 if (p->rChild && p->rChild != flag) { p = p->rChild; } else { st.pop(); visit(p); flag = p; p = nullptr; //經過迴圈條件後,直接再取棧頂 } } } }
3 應用
a 求給定某節點的所有祖先
i)思路分析:後序遍歷中,訪問到節點x時,當前棧中所有節點即為該節點的祖先。因此讀取棧中的內容即可。
但因為STL棧只提供top()介面,不方便順序訪問。故此處,用vector模擬棧的實現。
ii)關鍵分析:一旦找到節點之後,列印棧中所有元素,直接返回。
void Ancestor(BiTree T, std::vector<BiTNode*>& v, BTElemType e) { if (!T) { Error(); return; } v.clear(); BiTNode *p = T; BiTNode *pFlag = nullptr; while (T || !v.empty()) { if (p) { if (p->data == e) { //找到e時,該vector中所有元素即為e的ancestor for (auto iter = v.begin(); iter != v.end(); ++iter) { cout << (*iter)->data << " "; } return; } else { v.push_back(p); //模擬入棧 p = p->lChild; } } else { p = *(v.end() - 1); //模擬取棧頂 if (p->rChild && p->rChild != pFlag) { p = p->rChild; } else { v.pop_back(); //模擬出棧 pFlag = p; p = nullptr; } } } }
b 求給定兩個節點的最近公共祖先
i)思路分析:
先得到一個節點x的後序遍歷棧st1,把該棧拷貝到另一個棧中st2,作為該節點的後序遍歷棧
然後繼續遍歷,直到找到另一個元素y,此時st1棧中元素即為y的後序遍歷棧
最後,尋找兩個棧中最後一個相同的元素,即為兩個元素的最近公共祖先
ii)缺陷:
該方法的主要缺陷是:x必須出現在y左邊。
程式碼如下:
//尋找最近公共祖先主程式
BTElemType CommonAncestor(BiTree T, BTElemType x, BTElemType y) { std::vector<BiTNode*> st1, st2; BiTNode *p = T; BiTNode *pFlag = nullptr; BTElemType result = '?'; //標識是否有公共祖先 while (p || !st1.empty()) { if (p) { if (p->data == x) { //b把當前所有元素拷貝到st2中 st2 = st1; } if (p->data == y){ result = FindLastCommonElem(st1, st2, T); break; } st1.push_back(p); p = p->lChild; } else { p = *(st1.end() - 1); if (p->rChild && p->rChild != pFlag) { p = p->rChild; } else { st1.pop_back(); pFlag = p; p = nullptr; } } } return result; //呼叫者通過判斷 result == '?'來得出是否存在公共組選 }
//當找到x和y之後,查詢兩個棧中最後一個相同元素
BTElemType FindLastCommonElem(std::vector<BiTNode*>& st1, std::vector<BiTNode*>& st2, BiTree T) {
if (st1.size() == 0 || st2.size() == 0) { //x和y中至少有一個為根節點
return T->data;
}
BiTNode *pResult = nullptr;
//筆者起初想的用最簡單的辦法直接掃描兩個vector,然後找出最後一個公共元素,但時間複雜度是O(n^2),而此方法為O(n)
if (st1.size() < st2.size()) {
pResult = *(st1.end() - 1); //st1中最後一個元素即為公共元素
} else if (st1.size() > st2.size()){
pResult = *(st2.end() - 1); //st2中最後一個元素即為公共元素
} else {
auto iter1 = st1.begin();
auto iter2 = st2.begin();
for (; iter1 != st1.end(); ++iter1, ++iter2) {
if ((*iter1)->data != (*iter2)->data) {
break;
}
}
pResult = *(iter1 - 1);
}
return pResult->data;
}
4 總結
1 後序遍歷的非遞迴程式碼給我的啟示:在適當的時候做標記,
2 一旦掌握了後序遍歷非遞迴,那麼其的應用可以根據模版,適當的修改,即可解決問題。