已知先序遍历,后序遍历,求中序遍历

 1.题目描述

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

给定一棵有n个结点的二叉树的先序遍历与后序遍历序列,求其中序遍历序列。
若某节点只有一个子结点,则此处将其看作左儿子结点

示例1

输入

复制5,[3,2,1,4,5],[1,5,4,2,3]

5,[3,2,1,4,5],[1,5,4,2,3]

返回值

复制[1,2,5,4,3]

[1,2,5,4,3]

说明

 

 2.必备知识//来源牛客题解 | NC612#中序序列#_牛客博客 (nowcoder.net)

题目分析

在给定两个遍历序列,求另一种遍历序列中:
①给定先序序列和中序序列,可以唯一确定树,后序遍历唯一;
②给定后序序列和中序序列,可以唯一确定树,先序遍历唯一;
③给定先序序列和后序序列,不能唯一确定树;
前两种情况,能唯一确定树的原因是能够根据两个序***定根结点的左右子树范围(主要是中序序列分隔了左右子树);
第三种情况,不能确定左右子树的范围,因为当某结点只有一个子节点时,无法确定它是左子节点还是右子节点,如图,两种情况的先序和后序是相同的。
 

图片说明


当题目中表明,只有一个结点则看做左子节点,那就可以唯一确定树了。
当先序遍历i还未到末尾时,则将i作为根节点,i+1一定是它的左子节点(若存在子节点,首先考虑左子节点),根据左子节点 i+1的值,可以在后序遍历(左-右-根)中,获得左右子树的范围:
 

图片说明

图片说明

解题思路

方法1:重建二叉树,再进行中序遍历
重建二叉树的过程,就是确定根的左右子节点的过程,然后再继续遍历左右子节点,基本思路:
1.若存在子节点一定为左子节点,所以先序遍历中,根结点i后面若还有数,则i+1一定是左子节点;
2.根据i+1的值,在后序遍历中找到左子节点的位置 j ,然后确定左子树长度和右子树长度;
3.这里一定要限制每一次递归的数组范围,所以设置pl 和 pr,sl和sr来记录递归范围;
4.当在后序遍历中找到了左子节点的位置 j ,则在后序遍历中 sl ~ j是左子树范围,j+1 ~ sr是右子树范围,同时也可以知道在先序遍历中的范围(知道左右子树的长度了),pl+1 ~ pl+1+j-sl是左子树范围,pl+1+j-sl+1 ~ pr是右子树范围;
5.最后对子树进行同样的递归遍历,直到所有的都遍历完成,对数进行中序遍历得到结果。

方法2:不创建树,直接在遍历过程中,获取中序遍历序列
在方法1中,我们需要不断创建树结点,最后再重新遍历一遍树,才能得到结果,其实可以在遍历的过程中,直接使用结果数组记录遍历的值,因为是中序遍历,所以需要在所以的左子节点都建立完成后,才将值赋给res数组,这里需要使用全局变量index,来标记当前记录的数据个数。

3.代码

这里选择方法2,核心代码模式 

 

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param n int 二叉树节点数量
     * @param pre intvector 前序序列
     * @param suf intvector 后序序列
     * @return intvector
     */
    vector<int> inorder;//存储中序序列答案;
    
    void deal(int l1,int r1,vector<int> &pre,int l2,int r2,vector<int> &suf)
    {
        if(l1>r1) {return ;}
        else if(l1 == r1) {inorder.push_back(pre[l1]);
                           return ;}//l1==r1 即为叶子节点,直接压入答案;
        
        int index = -1;
        for(int i = l2;i<=r2;i++)
        {
            if(pre[l1+1] == suf[i])
            {
                index = i;
                break;//找到左子树的根节点,找到之后,在后序遍历中,index的左边即为index的子节点(包括左右子节点)。index的右边即为index的兄弟节点及其子节点
            }
        }
        
        deal(l1+1,l1+1+index-l2,pre,l2,index,suf);//递归左子树,其中:
        //l1+1是找到先序遍历的左子树根节点,index-l2是记录左子树根节点的子节点数量,因为后序中index的左边都为其子节点即index-1+l2+1;
        inorder.push_back(pre[l1]);//将根节点压入;
        deal(r1-(r2-1-index)+1,r1,pre,index+1,r2-1,suf);//r1-(r2-1-index)+1为找到右左子树的序列,包括根节点,括号内的为右子树的个数
    }
    
    
    vector<int> solve(int n, vector<int>& pre, vector<int>& suf) {
        // write code here
        deal(0,n-1,pre,0,n-1,suf);
        
        return inorder;
        
    }
};

  • 30
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值