二叉树遍历

设一个二叉树节点的树根为V,左右孩子分别为L,R。
按照某种次序访问树中的节点,每个节点被访问恰好一次。由局部根节点访问的次序可以分为3种遍历方式。

  • 先序遍历 V->L->R
  • 中序遍历 L->V->R
  • 后序遍历 L->R->V

先序遍历

递归实现

template <typename T,typename VST>
void traverse(BinNodePosi(T) x,VST &visit){
    if (!x) return;
    visit(x->data);
    traverse(x->lChild,visit);
    traverse(x->rChild,visit);
}

迭代实现

  • 在递归遍历中,每一个递归实例尽管只对应于一帧,但是因为它们必须具有通用格式,所以并不能做到足够小。我们完全可以使得每一帧做到足够小,尽管从big O意义上讲每一帧都可以看作一个常数,但是差异依旧巨大,所以很有必要改为迭代形式。
  • 通过引入栈来实现

方式1

template <typename T,typename VST>
void travPre_I1(BinNodePosi(T) x,VST &visit){
    Stack <BinBodePosi(T)> S;//辅助栈
    if (x) S.push(x); //根节点入栈
    while (!S.empty()){//当栈不为空时
        x = S.pop(); visit(x->data);//弹出并访问
        /**
         *先检查右孩子,先入后出
         *如果有右孩子,则右孩子入栈
         *如果有左孩子,则左孩子入栈
         */
        if(HasRChild(*x)) S.push(x->rChild);
        if(HasLChild(*x)) S.push(x->lChild);
    }
}

方式2

  • 对于任何一个子树,对于先序遍历来说,一旦树根节点被访问,那么接下来访问的就是它的左孩子,以及左孩子的左孩子,直到左孩子的分支到尽头。
  • 我们可以沿着这个左侧链出发,研究出来新的一种迭代算法
/**
 *用于遍历左侧链,并将其右孩子入栈
 */
template <typename T,typename VST>
static void visitAlongLeftBranch(//分摊O(1)
    BinNodePosi(T) x,
    VST & visit,
    Stack <BinNodePosi(T)> &S)
{
    while(x) {
        visit(x->data);//访问当前节点
        S.push(x->rChild);//将右孩子入栈
        x = x->lChild;//沿着左侧链下行
    }
}

template <typename T,typename VST>
void travPre_I2(BinNodePosi(T) x,VST & visit){
    Stack <BinNodePosi(T)> S;//辅助栈
    
    while(true){
        visitAlongLeftBranch(x,visit,S);//访问左侧链
        if(S.empty()) break;//栈空退出
        x = S.pop();//弹出下一子树的根
    }
}

中序遍历

递归实现

template <typename T,typename VST>
void traverse(BinNodePosi(T) x,VST &visit){
    if (!x) return;
    traverse(x->lChild,visit);
    visit(x->data);
    traverse(x->rChild,visit);
}

迭代实现

  • 通过观察访问的次序,我们可以发现:
    • 访问从根节点左孩子开始,直到左侧链到底,访问的第一个节点是最后一个不为空的左孩子。
    • 在任一局部,当控制权转给根节点,根节点不会马上访问,它会将控制权谦让给左孩子,直到它的左孩子接受访问后才会继续深入到它的左孩子的右子树中,而接下来才会返回根节点处。
/**
 *用于遍历左侧链,将经过的所有节点入栈
 */
template <typename T,typename VST>
static void goAlongLeftBranch(BinNodePosi(T) x,Stack <BinNodePosi(T)> &S)
{
    while(x) {//反复将其左孩子入栈,沿着左分支深入
        S.push(x);
        x = x->lChild;
    }
}

template <typename T,typename VST>
void travIn_I1(BinNodePosi(T) x,V &visit){
    Stack <BinNodePosi(T)> S;//辅助栈
    while(true){
        if (x == NULL) {
            if(S.empty()) break;//若栈为空则结束
            x = S.pop();//弹出
            continue;
        }
        goAlongLeftBranch(x,S);//将左分支全部入栈
        if(S.empty()) break;//若栈为空则结束
        x = S.pop();//弹出
        visit(x->data);//访问
        x = x->rChild;//转向其右子树
    }
}

后续遍历

递归实现

template <typename T,typename VST>
void traverse(BinNodePosi(T) x,VST &visit){
    if (!x) return;
    traverse(x->lChild,visit);
    traverse(x->rChild,visit);
    visit(x->data);
}

迭代实现

  • 通过观察访问的次序,我们可以发现:
    • 访问从根节点的左孩子开始,直到左分支的底部,然后再检查左分支底部是否有右孩子,如果有,继续将其右孩子当成根节点再进行当前这一段的步骤。
    • 直到一个节点既没有左分支也没有右分支,那么将访问它。这就是第一个访问的节点。
    • 左子树访问完成后,若右子树为空或者右子树已经访问过,这时候直接访问根节点。
template <typename T,typename VST>
void travAft_I1(BinNodePosi(T) x,V &visit){
    Stack <BinNodePosi(T)> S;//辅助栈
    BinNodePosi(T) prev = NULL;
    while (true){
        
        goAlongLeftBranch(x);//将左分支全部入栈
        if(S.empty()) break;//若栈为空则结束
        BinNodePosi(T) x = S.pop();
        if (!HasRChild(*x) || x->RChild == pre){
            //若右子树为空,或者右子树被访问过
            pre = x;//记录
            visit(top->data);//访问
        }
        else{
            x = x->right;//转到右分支
        }
    }
    
}

层次遍历

  • 所有节点按照深度次序,由高到低访问。
  • 引入队列来解决

迭代实现

template <typename T,typename VST>
void travLevel(BinNodePosi(T) x,VST &visit){
    Queue<BinNodePosi(T)> Q;//引入辅助队列
    Q.enqueue(x);//根节点入队
    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);
    }
}

打印分层

  • 要求将层次遍历的节点,每遍历到下一层,就进行一次换行。
    思路:
  • 使用last表示正在打印的当前行的最右节点,nlast表示下一行的最右节点。
  1. 先将根节点置为last,它肯定是第一层的最右节点。
  2. 每当节点入队一次,就将该节点置为nlast,若发现弹出的节点为last,则将last置为和nlast相同。表示下一行。
template <typename T,typename VST>
void travLevel(BinNodePosi(T) x,VST &visit){
    Queue<BinNodePosi(T)> Q;//引入辅助队列
    BinNodePosi(T) last;
    BinNodePosi(T) nlast;
    Q.enqueue(x);//根节点入队
    last = x;
    while(!Q.empty()){//当队列不为空
        BinNodePosi(T) x = Q.dequeue();//队首结点出队
        visit(x->data);//访问
        /**
         *将其左右孩子入队
         */
        if (HasLChild(*x)) {
        nlast = x->lChild;
        Q.enqueue(x->lChild);
        }
        if (HasRChild(*x)) {
        nlast = x->rChild;
        Q.enqueue(x->rChild);
        }
        
        if (x == last){
            last = nlast;
            nextline();//换行
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值