树的四种遍历(先序、中序、后序、层次)

一.树的先序遍历(迭代)

  • 首先我们要给出树的结点类,提供所有遍历通用的接口:
#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);//右孩子入队
  }
}

posted @ 2021-11-23 10:26  yytarget  阅读(656)  评论(0编辑  收藏  举报