樹的四種遍歷(先序、中序、后序、層次)


一.樹的先序遍歷(迭代)

  • 首先我們要給出樹的結點類,提供所有遍歷通用的接口:
#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);//右孩子入隊
  }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM