数据结构 --- 二叉树的先序、中序和后序遍历(非递归版本)

之前,我们写过二叉树的一些基本操作,大部分是通过递归来实现的。想想看,要是不采用递归的方式,我们该如何对二叉树进行操作?
下面,我们用非递归方式完成二叉树的先序、中序和后序遍历。

1.通过循环实现树的先序遍历

还是我们的那颗二叉树:
这里写图片描述
我们先想一下,先序遍历的顺序是:根结点 —> 左子树 —> 右子树。只要我们先打印根结点,然后再打印根结点的左右子树,每一个节点都这样循环的进行,直到遍历完所有的结点。

思路:这里,我们建一个栈,先将根结点入栈,对照上图,即 A 入栈,循环的取栈顶元素,出栈,并且访问栈顶元素,即第一次打印 A ,然后先将该元素的右子树入栈 即C,再将左子树 B 入栈,重复上述步骤,直到栈为空,说明遍历结束。
代码如下:


void TreePreOrderByLoop(TreeNode* root)
{
    if(root == NULL)
    {
        return;
    }
    //1.先把根节点入栈
    SeqStack stack;
    SeqStackInit(&stack);
    SeqStackPush(&stack,root);
    //2.循环的取栈顶元素,栈为空,说明循环结束
    TreeNode* cur;
    while(1)
    {
        int ret = SeqStackTop(&stack,&cur);
        if(ret == 0){
            break;
        }
        //a)取栈顶元素为当前元素;
        //b)出栈
        SeqStackPop(&stack);
        //c)访问当前元素
        printf("%c ",cur->data);
        //d)将当前点的右子树入栈
        if(cur->rchild != NULL)
        {
            SeqStackPush(&stack,cur->rchild);
        }
        //e)将当前点的左子树入栈
        if(cur->lchild != NULL)
        {
            SeqStackPush(&stack,cur->lchild);
        }
    }
    printf("\n");
    return;
}

2.通过循环实现树的中序遍历

这里写图片描述
思路:定义一个cur指针,先将cur 指向root,循环判断cur是否为空
如果不为空,入栈,然后将cur指向cur的 lchild;
如果为空,取栈顶元素,出栈并且访问它,再将cur指向栈顶元素的 rchild。直到栈为空,遍历结束。
具体实现代码如下:

void TreeInOrderByLoop(TreeNode* root)
{
    if(root == NULL)
    {
        return;//非法输入
    }
    SeqStack stack;
    SeqStackInit(&stack);
    //1.先将cur指向root 
    TreeNode* cur = root;
    //2.循环的判断cur是否为空,
    while(1){
        while(cur != NULL)
        {
            //  如果不为空,入栈,然后让cur指向cur的lchild
            SeqStackPush(&stack,cur);
            cur = cur->lchild;
        }
        // 如果为空,取栈顶元素,访问出栈,cur指向cur的rchild
        TreeNode* top = NULL;
        int ret = SeqStackTop(&stack,&top);
        if(ret == 0)
        {
            return;//栈为空,遍历完成
        }
        printf("%c ",top->data);
        SeqStackPop(&stack);
        cur = top->rchild;
    }
}

3.通过循环实现树的后序遍历

思路:后序遍历和中序遍历思路大体一致,但有一个很重要的区别:当取到栈顶元素后我们不能立即访问它,因为不确定它的右子树是否已经访问过。(后序遍历的顺序:左子树 —> 右子树 —>根节点)。因此,访问栈顶元素需要满足两个条件:1.当前节点右子树为空;2.右子树已经访问过了(即当前节点的上一个节点,后序遍历根节点前一个就是右子树)。
代码如下:

void TreePostOrderByLoop(TreeNode* root)
{
    if(root == NULL)
    {
        return;//非法输入
    }
    SeqStack stack;
    SeqStackInit(&stack);
    //1.先让cur指向root
    TreeNode* cur = root;
    //pre保存上一个访问过的元素
    TreeNode* pre = NULL;
    //2.循环的判断cur是否为空
    while(1){
        while(cur != NULL)
        {
            //3.如果不为空,压栈
            SeqStackPush(&stack,cur);
            //让cur指向cur的左孩子结点
            pre = cur;
            cur = cur->lchild;
        }
        //4.如果为空,循环取栈顶元素,对栈顶元素进行判定,
        TreeNode* top;
        int ret = SeqStackTop(&stack,&top);
        if(ret == 0)
        {
            //栈为空,说明遍历完成
            return;
        }
        //  a)如果栈顶元素的右子树访问过了,和访问的上一个元素是同一个
        //  b)或者栈顶元素没有右子树
        //  就访问栈顶元素,然后出栈
        if(top->rchild == NULL || top->rchild == pre)
        {
            printf("%c ",top->data);
            SeqStackPop(&stack);
            pre = top;
        }
        else
        {
            //5.如果不满足以上条件,就让cur指向栈顶元素的右子树,重复循环
            cur = top->rchild;
        }
    }
    return;
}

猜你喜欢

转载自blog.csdn.net/y6_xiamo/article/details/80386871
今日推荐