一.樹的先序遍歷(迭代)
- 首先我們要給出樹的結點類,提供所有遍歷通用的接口:
#define BinNodePosi(T) BinNode<T>* //結點位置
template<typename T>
struct BinNode{
BinNodePosi(T) parent, lChild, rChild; //父親和孩子結點
T data; //數據
int height; //高度
int size(); //子樹規模
BinNodePosi(T) insertAsLC(T const& );//作為左孩子插入新結點
BinNodePosi(T) insertAsRL(T const& );//作為右孩子插入新結點
BinNodePosi(T) succ(); //當前結點的直接后繼(中序遍歷意義下)
template<typename VST> void travLevel(VST& );//子樹層次遍歷
template<typename VST> void travPre(VST& );//子樹先序遍歷
template<typename VST> void travIn(VST& );//子樹中序遍歷
template<typename VST> void travPost(VST& );//子樹層次遍歷
};
- visit例程:
template<typename T, typename VST>
static void visitAlongLeftBranch(BinNodePosi(T) x, VST& visit, Stack<BinNodePosi(T)>& s){
while(x){
visit(x->data);//訪問當前結點
S.push(x->rChild);//右孩子入棧(將來逆序出棧)
x = x->lChild;//沿左側鏈下行
}//只有右孩子,NULL可能入棧—增加判斷剔除后者,不值得
}
- 主算法:
template<typename T, typename VST>
void travPre(BinNodePosi(T) x, VST& visit){
Stack<BinNodePosi(T)> S;//輔助棧
while (true){//以右子樹為單位,逐批訪問結點
visitAlongLeftBranch(x, visit, S);//訪問子樹x的左側鏈,右子樹入棧緩存
if(S.empty()) break;
x = S.pop();//彈出下一子樹的根
}
}
由於樹的先序遍歷的遞歸寫法為兩句尾遞歸,所以很容易將之轉換為迭代形式。
二.樹的中序遍歷(迭代)
- go例程:
template<typename T>
static void goAlongLeftBranch(BinNodePosi(T) x, Stack<BinNodePosi(T)>& S){
while(x){
S.push(x);//左孩子反復入棧,沿左分支深入
x = x->lChild;
}
}
- 主算法:
template<typename T, typename V>
void travIn(BinNodePosi(T) x, V& visit){
Stack<BinNodePosi(T)> S;//輔助棧
while(true){
goAlongLeftBranch(x, S);//從當前結點出發,逐批入棧
if(S.empty()) break;
x = S.pop();//X的左子樹或為空(或已遍歷),故
visit(x->data);//立即訪問它
x = x->rChild;//再轉向其右子樹(可能為空)
}
}
迭代的分攤分析復雜度為O(n),但常系數要比遞歸小的多。
三.樹的后序遍歷(迭代)
- goto例程:
template<typename T>
static void gotoLeftmostLeaf(Stack<BinNodePosi(T)>& S){
while(BinNodePosi(T) x = S.top())//自頂而下反復檢查棧頂結點
if(HasLChild(*x)){//HasLChild判斷是否有左孩子,盡可能向左,在此之前,
if(HasRChild(*x))//若有右孩子,則
S.push(x->rChild);//優先入棧(先進后出)
S.push(x->lChild);//然后轉向左孩子
}else //實不得已
S.push(x->rChild);//才轉向右孩子
S.pop;//返回之前,彈出棧頂的空結點
}
- 主算法:
template<typename T, typename VST>
void travPost(BinNodePosi(T) x, VST& visit){
Stack<BinNodePosi(T)> S;//輔助棧
if(x) S.push(x);//根結點首先入棧
while(!S.empty()){//x始終為當前結點
if(S.top() != x->parent)//若棧頂非x之父(而為右兄弟)
gotoLeftmostLeaf(S);//則在其右兄子樹中找到最靠左的葉子—無孩子的結點(遞歸深入)
x = S.pop();//彈出棧頂(即前一結點之后繼)以更新x
visit(x->data);//並隨之訪問
}
}
四.樹的層次遍歷(迭代)
- 主算法:
template<typename T, typename VST>
void travLevel(VST& visit){
Queue<BinNodePosi(T)> Q;//輔助隊列
Q.enqueue(this);//根結點入隊
while(!Q.empty()){
BinNodePosi(T) x = Q.dequeue();//取出隊首結點,並隨即
visit(x->data);//訪問之
if(HasLChild(*x)) Q.enqueue(x->lChild);//左孩子入隊
if(HasRChild(*x)) Q.enqueue(x->rChild);//右孩子入隊
}
}