剑指offer题解(C语言)----JZ07 根据先序与中序遍历结果重建二叉树

题目

给定节点数为 n 的二叉树的前序遍历和中序遍历结果,请重建出该二叉树并返回它的头结点。

提示:

1.vin.length == pre.length;

2.pre 和 vin 均无重复元素;

3.vin出现的元素均出现在 pre里;

4.只需要返回根结点,系统会自动输出整颗树做答案对比;

数据范围:0≤n≤2000,节点的值 −10000≤val≤10000;

要求:空间复杂度 O(n),时间复杂度 O(n)

解析

本题需要抓住3个关键点:

1. 先序遍历的第一个值一定是整棵树的根节点;

2. 使用先序遍历时,遍历左子树的过程依然是先序遍历,中序遍历同理;

3. 中序遍历中,根节点所在位置左侧的值位于左子树,右侧的值位于右子树;

因此,可以通过先序第一个数找到根节点,通过遍历找到根节点在中序遍历中的位置iSite。

当我们确定了根节点在中序遍历中的位置后,开始考虑左右两子树的构建。

首先考虑左子树。左子树的根节点根据先序遍历“中左右”的顺序,应为目前先序遍历中,整棵树的根节点所在位置的后一位,即pre+1,在中序遍历中因为先遍历左子树,因此中序遍历序列的第1位就是左子树。

又因为在先序遍历中,一定会遍历完整棵左子树后再遍历右子树,又因为中序遍历iSite的左边都是左子树,因此构建左子树时,在先序遍历中的长度仅为iSite,在中序遍历中的长度也为iSite。

综上,每一次构建左子树时,都应当是:

 tree->left = reConstructBinaryTree(pre + 1, iSite, vin, iSite);

再看右子树。右子树的根节点根据先序遍历“中左右”的顺序即为,根节点+左子树长度的后一位,pre+iSite+1,在中序遍历中,根据“左中右”的顺序,位于vin+iSite+1的位置。

而我们不知道左右子树是否有一样多的元素,因此不能盲目地认为右子树元素个数也为iSite。而是应当写为preLen-iSite-1,或vinLen-iSite-1。因preLen = vinLen,因此二者除了阅读外,值相等。

综上,每一次构建右子树时,都应当是:

tree->right = reConstructBinaryTree(pre + iSite + 1, preLen - iSite - 1, vin + iSite + 1, vinLen - iSite - 1);

二叉树的问题都可以归结为左右子树反复递归的过程,因此明确了根节点和左右子树的复建规则后,就可以写代码了。

代码

//pre是先序遍历,vin是中序遍历
struct TreeNode* reConstructBinaryTree(int* pre, int preLen, int* vin, int vinLen ) {
    struct TreeNode* tree = 0; //新建一棵二叉树
    int iSite = 0; //表示右子树的深度,遍历完成后代表根节点所在位置
     
    if ((NULL == pre) || (0 == preLen))
        return NULL; //若为空树,则返回NULL
     
    tree = (struct TreeNode*)malloc(sizeof(struct TreeNode)); //调用一块空间来建立树
    memset(tree, 0, sizeof(struct TreeNode)); //为新申请的内存做初始化工作
    tree->val = pre[0]; //根节点一定是先序遍历的第一个值
    while (pre[0] != vin[iSite])
    {
        iSite++;
    } //在中序遍历中找到根节点所在的位置
    //构建左子树,递归
    tree->left = reConstructBinaryTree(pre + 1, iSite, vin, iSite); 
     //构建右子树,递归
    tree->right = reConstructBinaryTree(pre + iSite + 1, preLen - iSite - 1, vin + iSite + 1, vinLen - iSite - 1);
     
    return tree;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值