第三十天:二叉树的后序遍历

今天是释然发题解的第三十天,以后会经常和大家分享学习路上的心得,希望和大家一起进步,一起享受coding的乐趣
本文约2100字,预计阅读7分钟
很多天前我们学习了二叉树,忘记的小伙伴们可以看一下哦:
二叉树

今天我们来聊一聊二叉树的后序遍历,明天和大家分享线段树问题的相关题目:

非递归的后序遍历

实现

我们想一下假如说一棵树是这样的:

[
3,9,20,null,null,15,7
]

这棵树应该长这样:

    3
   / \
  9  20
    /  \
   15   7

二叉树的后序遍历是比较复杂的,复杂在哪呢?

最重要的问题就是,节点的值该何时打印的问题。对于前序遍历来说,每一次直接打印然后找右子树和左子树,不停地打印栈顶元素就行。对于中序遍历来说,如果左子树始终有结点存在,那么第一个输出的结点门就应该是最左下角的那个,所以当我们把根节点压入栈的时候,我们就需要不断地把左边界压入栈,即不停地

temp=temp.left;

直到找到的那个结点为空为止,这个时候就弹出这个结点,开始找这颗子树的右结点的左子树(虽然讲的非常变扭,其实自己画个图就好)然后不停地找左子树

为什么要不停地找左子树呢?很简单,中序遍历最先需要遍历的就是左子树的左孩子,但是一个结点对于自己的左孩子来说其实就是父亲,不能第一个打印,因此必须不停地找左孩子
最后栈为空时,停止

那么对于后序遍历来说,同样是根节点压栈,但是什么时候打印根节点呢?

我一开始是这样想的,如果也是先压右节点,再压左节点,这样是符合遍历的顺序的,但是什么时候开始打印呢?应该是根节点左子树的最左边的叶子结点开始打印.既然这样,我们就先找到那个第一个需要打印的结点,然后从栈里面打印就行。

其实这是不合理的,当我们先压右节点,再压左节点的时候,此时再想压右节点的孩子就不方便了。这个时候,我们有两种解决方案:
第一个就是再开一个栈,我们去改变它的压入的顺序,先不停地压左节点,再压右节点,每一次弹出到第二个栈里面,就可以保证所有的结点都可以找到,然后利用第二个栈去打印。
第二个就是,有标记的去压栈,现在最麻烦的就是这个结点的左孩子和右孩子有没有打印过,我们知道只要根节点打印过了,那么就不需要去找这个结点的左右孩子了。相当于额外使用一个指针,代表上一次打印的结点。

实现

//非递归的后序遍历
//选择两个栈的方法
#include<stack>

void PosOrder(Node root)
{
    if (root == null)
    {
        return;
    }
    stack<Node> stack1;
    stack<Node> stack2;
    //vector<int> result;
    // 先把根节点压栈
    stack.push(root);
    Node cur;
    while (!stack1.empty())
    {
        cur=stack.pop();
        stack2.push(cur);
        //root = stack.top();
        //cout<<root.val;
        // 先压左节点
        if (root.left != NULL)
        {
            stack.push(root.left);
            //root = root.right;
        }
        // 再压右节点
        if (root.right != NULL)
        {
            stack.push(root.right);
        }
    }
    
    while(!stack2.empty())
    {
        cout<<stack2.pop();
    }

    cout<<" "<<endl;
}

//二叉树的后序遍历
//只使用一个栈的情况
#include <stack>

void PosOrder(Node root)
{
    if (root == NULL )
    {
        return;
    }
    stack<Node> sta;
    sta.push(root);
    Node cur=NULL;
    //root 是上一次打印过的结点
    //cur 是当前的栈顶结点
    while(!sta.empty())
    {
        cur=sta.top();//这个时候不能弹出,因为不知道
        //这个结点的左子树和右子树是否需要压入栈中
        //考虑左子树是否需要压栈
        if (cur.left != NULL && root!=c.left && h!=cur.right)
        {
            sta.push(cur.left);
        }
        //考虑右子树是否需要压栈
        else if(c.right != NULL&& h!=cur.right)
        {
            sta.push(cur.right);
        }
        else
        {
            cout<<cur.value;
            sta.pop();
            h=cur;
        }
    }

    printf("\n");
}

这个过程不好想,看了好久才弄懂,哎慢慢来,以后精力就在这方面了。

好了,今天的二叉树的后序遍历就到这里。
释然每天发布一点自己学习的知识,希望1年后我们也能在ACM的赛场上见面,一起去追寻自己的程序猿之路吧!

后期也会和大家一起分享学习心得和学习经验呢,明天我们不见不散哦!

近期预告:
线段树、树状数组、状压DP、Trie树、图论

如果大家有什么建议或者要求请后台留言
联系方式:shirandexiaowo@foxmail.com

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 题目描述: 给定一棵二叉树后序遍历和中序遍历结果,输出该树的先序遍历结果。 输入格式: 第一行给出正整数n(≤30),是树中结点的个数。随后两行,每行给出n个整数,分别对应后序遍历和中序遍历结果。 输出格式: 输出该树的先序遍历结果,数字间有一个空格,行末不得有多余空格。 输入样例: 7 2 3 1 5 7 6 4 1 2 3 4 5 6 7 输出样例: 4 1 3 2 6 5 7 解题思路: 根据后序遍历和中序遍历的结果,可以确定根节点和左右子树的中序遍历结果。然后根据左右子树的中序遍历结果,可以得到左右子树的后序遍历结果。递归处理左右子树,最后得到整棵树的先序遍历结果。 代码实现: ### 回答2: 题目描述: 本题给定一棵二叉树后序遍历和中序遍历结果,要求输出该树的先序遍历结果。 输入格式: 第一行给出正整数n(≤30),是树中结点的个数。 随后两行,每行给出n个整数,分别对应后序遍历和中序遍历结果。 解题思路: 对于二叉树的遍历问题,通常采用递归的方法处理。 我们可以根据后序遍历的特性,确定该数组最后一个元素就是根节点,然后在中序遍历数组中查找根节点,从而可以确定左子树和右子树的节点数目。 然后,我们再递归地处理左子树和右子树,并依次输出先序遍历结果。 时间复杂度:O(n^2) 代码实现: ### 回答3: 本题需要我们根据给定的一棵二叉树后序遍历和中序遍历结果,输出该树的先序遍历结果。题目包含三个关键词:后序遍历、中序遍历和先序遍历,这三种遍历方式都是二叉树遍历的方式。其中,后序遍历指的是先遍历左子树,再遍历右子树,最后遍历根节点,中序遍历指的是先遍历左子树,再遍历根节点,最后遍历右子树,而先序遍历则是指先遍历根节点,再遍历左子树,最后遍历右子树。通过后序遍历和中序遍历结果,我们可以还原出完整的二叉树,然后再进行先序遍历输出。 由于是后序遍历,最后一个节点一定是根节点,由此可以将中序遍历结果分为根节点的左子树和右子树。对于后序遍历来说,根节点的前一个节点是右子树的根节点,而根节点之前的所有节点都是左子树的节点。因此,我们可以不断地递归去解决左右子树的问题。 具体做法如下: 1.读入后序遍历和中序遍历的结果,存储到数组中。创建一个函数,传入后序遍历和中序遍历数组、左右子树的起止下标,返回当前子树的根节点。 2.在函数中,先找到当前子树的根节点,即后序遍历的最后一个节点,然后在中序遍历的结果中搜索该节点,找到该节点的位置,即为当前子树的根节点位置。 3.根据该节点位置,可以得到该节点左右两边分别属于左子树和右子树,然后递归调用该函数,分别求出左右子树的根节点。 4.输出当前子树的根节点,并将其返回。 5.在主函数中调用该函数,输出整棵树的先序遍历结果。 代码实现如下(Python):

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Shirandexiaowo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值