基础闯关记-二叉树递归和非递归遍历

二叉树的递归和非递归遍历

二叉树的遍历方式

由于二叉树不是线性结构,所以遍历方式可能有多样,二叉树的遍历方式一共有四种情况:先序遍历,中序遍历,后序遍历,层次遍历。所有的遍历都是从根节点出发

先序遍历

先序遍历的过程为:

  1. 访问根节点
  2. 先序遍历其左子树
  3. 先序遍历其右子树

先序遍历的先序可以理解为根先序,也就是优先访问根节点。这是一个递归定义,先根节点,然后左子树,然后右子树。下面这幅图二叉树

image

按照定义,这课二叉树先序遍历的结果:5314768

中序遍历

中序遍历的过程为:

  1. 中序遍历左子树
  2. 访问根节点
  3. 中序遍历右子树

中序遍历可以理解为根中序,也就是先访问左右子树,再访问根节点,同样这也是一个递归定义,所以这种遍历一定是左子树元素排在前面。下面这幅图中二叉树

image

中序遍历结果为:1345678,注意观察,是不是已经排好序了。

后序遍历

后序遍历的过程为:

  1. 后序遍历其左子树
  2. 后序遍历其右子树
  3. 访问根节点

后序遍历可以理解为根后序,也就是先访问左子树,右子树,最后访问根节点,注意这也是一个递归的定义。所以这种遍历根节点一定排在最后。下面这幅图中二叉树

image

后序遍历的结果为:1436875

遍历的递归实现

按照各种遍历的定义,可以很容易写出递归遍历算法,这里直接写出代码

//先序遍历
void PrePrint(BinTree Root){
    if(Root){
        printf("%d", Root->Data);
        PrePrint(Root->Left);
        PrePrint(Root->Right);
    }
}

//中序遍历
void InPrint(BinTree Root){
    if(Root){
        InPrint(Root->Left);
        printf("%d", Root->Data);
        InPrint(Root->Right);
    }
}

//后序遍历
void PostPrint(BinTree Root){
    if(Root){
        PostPrint(Root->Left);
        PostPrint(Root->Right);
        printf("%d", Root->Data);
    }
}

遍历的非递归实现

由于递归函数本身就是用栈实现的,所以我们可以自己使用栈模拟递归函数的写法,然后实现二叉树的遍历。我们使用中序遍历作为例子,讲解非递归遍历二叉树。

中序遍历的非递归实现

想象我们从根节点出发,我们每遇到一个节点都可能是根节点,所以我们遇到的节点得先压栈,一定要左子树遍历完毕,我们才能打印根节点的值。所以,中序遍历的步骤

  1. 遇到一个节点,就把它压栈,然后遍历左子树
  2. 当左子树遍历结束后,就可以弹出一个元素并访问它
  3. 然后右子树同样使用中序遍历算法遍历
//中序遍历的非递归实现
void InPrintWithStack(BinTree Root){
    BinTree T = Root;
    Stack S = initStack();
    //只要树,或者栈不为空,就一直做这个操作
    while(T || !isEmpty(S)){
        while(T){
            //一直向左走,直到底
            push(S, T);
            T = T->Left;
        }
        if(!isEmpty(S)){
            //弹出一个元素
            T = pop(S);
            printf("%d", T->Data);
            T = T->Right;//结尾一定是这句话,右子树重新来一遍循环
        }
    }
}

使用下图的二叉树为例,我们演算一遍中序遍历的非递归实现。

image

  1. 首先到5节点,注意中序遍历不能访问,压栈
  2. 继续向左走,遇到3节点,中序遍历不能访问,压栈
  3. 继续向左走,遇到1节点,中序遍历不能访问,压栈
  4. 继续向左走,为空了,说明到底了,弹出一个元素,就是1号节点,然后打印1,并向右走(遍历右子树)
  5. 1号节点向右也没有元素了。这一轮遍历结束
  6. 再弹出一个元素,3号节点,打印3,然后转向右子树遍历
  7. 在右子树种,打印4
  8. 右子树结束之后,再次出栈,打印5,然后又转向又子树

前序遍历的非递归实现

参照中序遍历的非递归实现,其实中序遍历中节点的访问顺序就是前序遍历需要的顺序,所以,前序遍历只要碰到节点就访问它,最终结果就是前序遍历的结果

void PrePrint2(BinTree Root){
    BinTree T = Root;
    Stack S = initStack();

    //二叉树或者栈不为空,就一直做这个操作
    while(T || !isEmpty(S)){
        //一直向左走,到头
        while(T){
            push(S, T);
            printf("%d", T->Data);//碰到元素就打印,这就是前序遍历的顺序
            T = T->Left;
        }
        if(!isEmpty(S)){
            T = pop(S);
            T = T->Right;//结尾一定要有T赋值右子树,右子树重新来一遍上面的操作
        }
    }
}

后序遍历的非递归实现

三种非递归遍历中,属后续遍历最复杂。仔细想一下,它为什么复杂。待续...

转载于:https://www.cnblogs.com/jianbingguozi/p/7348497.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值