CD71.【C++ Dev】二叉树练习题3 (从前序与中序遍历序列构造二叉树、从中序与后序遍历序列构造二叉树)

目录

1.知识回顾

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

分析

代码

提交结果

3.从中序与后序遍历序列构造二叉树

分析

代码

★细节提醒

提交结果


1.知识回顾

利用遍历顺序构造二叉树:CE14.【C++ Cont】练习题组15(二叉树专题) [NOIP 2001 普及组] 求先序排列

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

https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/description/
 

给定两个整数数组 preorderinorder ,其中 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 保证 为二叉树的中序遍历序列

分析

由于和CE14.【C++ Cont】练习题组15(二叉树专题)的[NOIP 2001 普及组] 求先序排列题类似,这里简单分析:

前序遍历: 根→左子树→右子树

中序遍历: 左子树→根→右子树

前序遍历最开始出现的就是根,拿到根后去后序遍历中划分左子树和右子树的区间

结论:前序确定根,中序确定左右子树区间

例如LeetCode的测试用例,见下图:

即[9],[3],[15,20,7],再向preorder后看到20,说明3的右子树的根节点是20(可以继续细分,代码中需要递归),.....(代码中可以设置一个指针prev_i来遍历preorder)

需要设置begin和end来标记inorder中需要递归的区间:

注意控制prev_i不能越界

可以得出二叉树的样子:

如果只给了preorder = [3,9,20,15,7],那么单凭前序遍历是不能断定9是左子树的根的,有可能左子树是空树,可能像这样:

代码

class Solution {
public:
    TreeNode* recursive_construct(vector<int>& preorder, vector<int>& inorder, int begin, int end)
    {
        if (begin > end)//如果区间不存在就返回
            return nullptr;
        TreeNode* newnode = new TreeNode(preorder[pre_i]);
        //先获取到preorder[pre_i]在inorder的下标,在[begin,end]中找根
        int root_i = begin;
        for (; root_i <= end; root_i++)
        {
            if (preorder[pre_i] == inorder[root_i])
                break;
        }
        //由于要执行preorder[pre_i] == inorder[root_i]判断,因此pre_i必须在判断结束后++
        pre_i++;
        //划分区间: [begin,root_i-1],[root_i],[root_i+1,end]
        //各自递归,前提:区间是存在的
        newnode->left = recursive_construct(preorder, inorder, begin, root_i - 1);
        newnode->right = recursive_construct(preorder, inorder, root_i + 1, end);
        return newnode;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
    {
        TreeNode* root = recursive_construct(preorder, inorder, 0, inorder.size() - 1);
        return root;
    }
private:
    int pre_i = 0;
};

注意到没有判断pre_i是否越界,因为pre_i一定不可能越界

recursive_construct每调用一次,就创建一个新节点,pre_i就++,

那么recursive_construct调用的次数==节点的数目==pre_i的值,当buildTree函数的root得到值后,pre_i的值为preorder.size(),虽然越界,但recursive_construct函数不再执行

提交结果

3.从中序与后序遍历序列构造二叉树

https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/description/
 

给定两个整数数组 inorderpostorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

示例 1:

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

示例 2:

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

提示:

  • 1 <= inorder.length <= 3000
  • postorder.length == inorder.length
  • -3000 <= inorder[i], postorder[i] <= 3000
  • inorder 和 postorder 都由 不同 的值组成
  • postorder 中每一个值都在 inorder 中
  • inorder 保证是树的中序遍历
  • postorder 保证是树的后序遍历

分析

和从前序与中序遍历序列构造二叉树思路类似

中序遍历: 左子树→根→右子树

后序遍历: 左子树→右子树→根

后序遍历的最后元素一定是根,那么有以下结论

结论:后序确定根,中序确定左右子树区间

代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* recursive_construct(vector<int>& inorder, vector<int>& postorder, int begin, int end)
    {
        if (begin > end)
            return nullptr;
        TreeNode* newnode = new TreeNode(postorder[post_i]);
        int root_i = begin;
        for (; root_i <= end; root_i++)
        {
            if (postorder[post_i] == inorder[root_i])
                break;
        }
        post_i--;
        newnode->right = recursive_construct(inorder, postorder, root_i + 1, end);
        newnode->left = recursive_construct(inorder, postorder, begin, root_i - 1);
        return newnode;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
    {
        post_i = postorder.size() - 1;
        //后序确定根,中序确定左右子树区间
        TreeNode* root = recursive_construct(inorder, postorder, 0, inorder.size() - 1);
        return root;
    }
private:
    int post_i;
};

post_i从后往前遍历postorder

★细节提醒

先递归右子树再递归左子树,这里和从前序与中序遍历序列构造二叉树题的代码不同

newnode->right = recursive_construct(inorder, postorder, root_i + 1, end);
newnode->left = recursive_construct(inorder, postorder, begin, root_i - 1);

提交结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zhangcoder

赠人玫瑰手有余香,感谢支持~

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

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

打赏作者

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

抵扣说明:

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

余额充值