非递归实现二叉树的遍历

前言

二叉树的遍历是按一定规律对二叉树的每个结点访问且仅访问一次。
二叉树的遍历顺序有以下三种,分别为:
前序遍历:根左右
中序遍历:左根右
后序遍历:左右根
二叉树的递归遍历比较简单,但是非递归遍历的难度就要上一个台阶了,尤其是后序遍历。
接下来我们就看他们的具体实现方法。

前序遍历

编程思想:借助栈实现,利用栈先入后出的特性
1.访问且打印当前节点
2.如果有右孩子,则右孩子进栈
3.如果有左孩子,直接打印;如果没有左孩子,栈顶元素出栈,直到栈为空。

代码实现:

void BinaryTreePrevOrderNonR(BTNode* root)
{
          Stack st;
          BTNode * cur = root;
          StackInit(&st, 100);
          while (cur)
          {
                    putchar(cur->data);
                    if (cur->rchild)//如果有右孩子就进栈
                    {
                              StackPush(&st, cur->rchild);
                    }
                    if (cur->lchild)//如果有左孩子就直接输出左孩子
                    {
                              cur = cur->lchild;
                    }
                    else//如果没有左孩子,栈顶元素出栈
                    {
                              cur = StackTop(&st);
                              StackPop(&st);
                    }
          }
          StackDestory(&st);
}

前序遍历相对来说容易理解,代码也比较简短,接下来看难度稍微增加一点的中序遍历。

中序遍历

编程思想:借助栈的先入后出原理
1.从当前结点开始遍历其左子树,直到左子树为空,并让所有结点入栈
2.取出栈顶元素并打印,访问其右子树,如果有右子树,重复过程1
3.如果没有右子树,重复过程2

代码实现:

void BinaryTreeInOrderNonR(BTNode* root)
{
          BTNode * cur = root;
          Stack st;
          StackInit(&st, 100);
          while (cur || !StackIsEmpty(&st))
          {
                    for (; cur; cur = cur->lchild)//将当前结点及他的左孩子们入栈
                    {
                              StackPush(&st, cur);
                    }
                    cur = StackTop(&st);
                    //1、如果右孩子为空,for循环不进,直接取栈顶
                    //2、如果右孩子不为空,那么这是一个没有左孩子的二叉树
                    //第一种情况为左子树访问完毕,第二种情况是左子树为空,无论是哪一种情况,当前结点都要打印
                    putchar(cur->data);
                    StackPop(&st);
                    cur = cur->rchild;
          }
          StackDestory(&st);
}

完成了中序遍历的过程,那么重点来了

后序遍历

编程思想:借助栈先入后出的原理。
首先设置一个标记为0,只有该标记为栈顶元素时被置位为1,
1.遍历当前节点的左子树,所有结点入栈,直到左子树为空,此时所有结点的标记均为0
2.访问栈顶元素,并将栈顶元素置位,访问其右子树,若该结点有右子树,重复过程1,若该结点没有右子树,进入过程3
3.取出栈顶元素,并检查其父亲结点,若标记已经置位为1,则一并打印,直到第一个标记为0的结点,重复过程2

代码实现:

void BinaryTreePostOrderNonR(BTNode* root)
{
          char tag[64];
          BTNode * cur = root;
          Stack st;
          StackInit(&st, 100);
          do{
                    for (; cur; cur = cur->lchild)//类似中序
                    {
                              StackPush(&st, cur);
                              tag[st.size - 1] = 0;//重置左子树的访问标记(LT)
                    }
                    while (!StackIsEmpty(&st) && tag[st.size - 1])//前面的条件只在最后一次循环跳出的时候生效
                    {
                              cur = StackTop(&st);
                              putchar(cur->data);
                              StackPop(&st);
                    }
                    if (!StackIsEmpty(&st))
                    //此条件只在最后一次循环跳出的时候生效
                    //后面的条件分两个情况
                    //1、当此次的cur为空时,上面的for不进,此条件成立
                    //2、当cur不为空时,上面的for进,此条件不成立
                    //当检测到当前的LT被置位(情况1),那么打印当前节点后,再去直接检查上一个结点(父节点)是不是也要被打印了,直到第一个标记为0的结点
                    {
                              cur = StackTop(&st);
                              //1、如果上面的while进了,那么证明左子树访问完毕
                              //2、如果上面的while每进,那么证明没有左子树
                              tag[st.size - 1] = 1;
                              cur = cur->rchild;
                              //左子树访问完毕后,访问右子树
                    }
          } while (!StackIsEmpty(&st));//由于后序遍历根结点是左后出栈的,所以在根结点出栈前,栈不可能为空,所以栈空为循环跳出条件
          StackDestory(&st);
}

总结

二叉树的三种非递归遍历都完成了,前序遍历和中序遍历都比较容易理解,但是后序遍历的难度就大大提升了,但是多理解几遍,对照过程做几道例题就可以掌握了,代码只是作为参考,可能还有更好的方法,欢迎大家一起交流。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值