从前序与中序遍历序列构造二叉树

问题

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

示例 1:

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]

示例 2:

输入: preorder = [-1], inorder = [-1]
输出: [-1]

提示:

  • 1 <= preorder.length <= 3000
  • inorder.length == preorder.length
  • -3000 <= preorder[i], inorder[i] <= 3000
  • preorder 和 inorder 均 无重复 元素
  • inorder 均出现在 preorder
  • preorder 保证 为二叉树的前序遍历序列
  • inorder 保证 为二叉树的中序遍历序列

解析

从二叉树的性质可以知道,先序可以确定跟,中序可以根据根的位置分割左子树和右子树。

根据这个性质及区间的划分来构建一棵二叉树。

二叉树的经验

1.往往需要递归解决问题

2.存在区间的递归往往需要借助区间来完成递归条件的返回。(控制返回语句)

代码

    
    TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder,
    int& prei, int inbegin, int inend)  //保证prei每次都是相同的prei
    {       
            //返回
        if (inbegin > inend)      //等于表示还有一个
            return nullptr;
        
            //去中序遍历找根
        int rooti = 0;
        while (inbegin <= inend)
        {
            if (inorder[rooti] == preorder[prei])  //preorder[previ]为根
                break;
            
            rooti++;    //保证跳出循环时rooti的下标就是根的下标,而不是再++
        }

        //先序确定根
        TreeNode* newnode = new TreeNode(preorder[prei++]);
        
        //中序分割左右子树的区间
        //分割成了 [inbegin, rooti - 1]   rooti   [rooti + 1, inend];
        newnode->left = _buildTree(preorder, inorder, prei, inbegin, rooti - 1);
        newnode->right = _buildTree(preorder, inorder, prei, rooti + 1, inend);

        return newnode;     //返回到上一层的递归处,完成链接(注意返回类型)
    }


class Solution {
public: 
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {

        TreeNode* root = nullptr;
        int prei = 0;
        int inbegin = 0;
        int inend = preorder.size() - 1;
        root = _buildTree(preorder, inorder, prei, inbegin, inend);
        return root;
    }
};

从返回类型可以看出,需要构建一个二叉树,用一个root指针指向这个二叉树并返回。

需要注意的是prei需要引用传参,保证访问的是线序列表的同一个prei

代码解剖

1.递归返回

借助区间完成

            //返回
        if (inbegin > inend)      //等于表示还有一个
            return nullptr;
        

2.借助先序根的位置去中序找到根,以完成区间的划分

 //去中序遍历找根
        int rooti = 0;
        while (inbegin <= inend)
        {
            if (inorder[rooti] == preorder[prei])  //preorder[previ]为根
                break;
            
            rooti++;    //保证跳出循环时rooti的下标就是根的下标,而不是再++
        }

3.先序确定根

   //先序确定根
        TreeNode* newnode = new TreeNode(preorder[prei++]);

4.中序划分区间,分割成了 [inbegin, rooti - 1]   rooti   [rooti + 1, inend];

//中序分割左右子树的区间
        //分割成了 [inbegin, rooti - 1]   rooti   [rooti + 1, inend];
        newnode->left = _buildTree(preorder, inorder, prei, inbegin, rooti - 1);
        newnode->right = _buildTree(preorder, inorder, prei, rooti + 1, inend);

5.返回        最后的返回一定不能忘!

     return newnode;     //返回到上一层的递归处,完成链接(注意返回类型)

为什么能完成链接呢?

就是因为在连接处进行递归调用,同时下一层递归调用中返回了root指针。从而在连接处层层递归调用,再返回到链接处。

整体的连接逻辑可以理解为:新建指针、链接处递归、下一层递归函数、新建指针、执行完函数中间部分的语句、返回指针到上一层的链接部分的递归调用处。以此完成链接。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值