C++手撕LeetCode——二叉树递归(根据前中后序构造)

        大三寒假要结束了,继续备战秋招,年前刷了些双指针、数组、链表的简单LeetCode题,都没有做笔记,现在也忘得差不多了,计划写一份专栏记录刷题的过程,复盘算法中的细节,由易到难,先刷简单题,再斩中等题!  


         一转眼就到4月份了,投递了好多暑期实习,4月份笔面试就会陆续开启,距离秋招也越来越近,这段时间在刷LeetCode Hot 100,每天刷个两道mid题,同时作个笔记,作为复盘,也作为到时笔面试前的复习资料

目录

一、关键思路:递归

二、递归法优点

三、例题训练

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

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


一、关键思路:递归

        递归是一种算法或函数的设计技巧,通过在函数定义中调用自身来解决问题。在数据结构与算法中,递归通常用于解决可分解为多个相似子问题的问题。递归算法通常包含两部分:基本情况和递归情况。

        基本情况是递归的出口,即在问题可以直接解决或不需要递归时的情况。

        递归情况则是指通过将问题分解成更小的子问题,并将子问题递归地解决来解决原始问题的情况

二、递归法优点

 递归算法通常具有简洁、优美和可读性强的优点,在编写递归时可以不用过于注重代码逻辑的具体细节,只要注意基本情况和递归情况的设计,以及递归深度的控制

三、例题训练

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

给定两个整数数组 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]

来源:力扣(LeetCode) 链接:力扣 

  • 实现思路:

首先,使用一个哈希表存储中序遍历序列中每个节点的位置,便于在递归构造二叉树时快速找到根节点的位置。

其次,递归构造二叉树。递归函数的参数包括当前子树在前序遍历序列中的起始位置、子树在中序遍历序列中的起始位置和结束位置。在递归函数中,首先根据当前子树在前序遍历序列中的起始位置得到根节点的值,然后在中序遍历序列中找到根节点的位置,将中序遍历序列分为左右两部分,分别递归构造左右子树,最后将左右子树挂在根节点上并返回根节点。

  • 核心思路:

根据前序遍历的特点找到根节点的值,再在哈希表中快速定位到根节点的下标,进而可以得出左子树的长度,接着可以计算出后序遍历和中序遍历中左右子树的长度,根据这些参数递归调用myBuildTree(),构造左右子树

  • C++代码实现:
/**
 * 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 {
private:
    unordered_map<int, int> index;
​
public:
    TreeNode* myBuildTree(const vector<int>& preorder, const vector<int>& inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
        if (preorder_left > preorder_right) {
            return nullptr;
        }
        
        // 前序遍历中的第一个节点就是根节点
        int preorder_root_index=preorder_left;
        // 在中序遍历中定位根节点
        int inorder_root_index=index[preorder[preorder_root_index]];
        // 先把根节点建立出来
        TreeNode* root = new TreeNode((preorder[preorder_root_index]));
        // 得到左子树中的节点数目
        int size_left_subtree = inorder_root_index-inorder_left;
        // 递归地构造左子树,并连接到根节点
        // 先序遍历中「从 左边界+1 开始到 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
        root->left=myBuildTree(preorder,inorder,preorder_left+1,preorder_left+size_left_subtree,inorder_left,inorder_root_index-1);
        // 递归地构造右子树,并连接到根节点
        // 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
        root->right=myBuildTree(preorder,inorder,preorder_left+size_left_subtree+1,preorder_right,inorder_root_index+1,inorder_right);
        return root;
    }
​
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n = preorder.size();
        // 构造哈希映射,帮助我们快速定位根节点
        for (int i = 0; i < n; ++i) {
            index[inorder[i]] = i;
        }
        return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
    }
};

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

给定两个整数数组 inorder 和 postorder ,其中 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]

来源:力扣(LeetCode) 链接:力扣 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  • 实现思路:

首先,使用一个哈希表存储中序遍历序列中每个节点的位置,便于在递归构造二叉树时快速找到根节点的位置。

其次,递归构造二叉树。递归函数的参数包括当前子树在后序遍历序列中的起始位置、子树在中序遍历序列中的起始位置和结束位置。在递归函数中,首先根据当前子树在后序遍历序列中的起始位置得到根节点的值,然后在中序遍历序列中找到根节点的位置,将中序遍历序列分为左右两部分,分别递归构造左右子树,最后将左右子树挂在根节点上并返回根节点。

  • 核心思路:

根据后序遍历的特点找到根节点的值,再在哈希表中快速定位到根节点的下标,进而可以得出左子树的长度,接着可以计算出后序遍历和中序遍历中左右子树的长度,根据这些参数递归调用myBuildTree(),构造左右子树

  • C++代码实现:
/**
 * 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:
    unordered_map<int,int>index;
    TreeNode* myBuildTree(const vector<int>&postorder,const vector<int>&inorder,int postorder_left,int postorder_right,int inorder_left,int inorder_right)
    {
        if(postorder_left>postorder_right)
        return NULL;
​
         // 后序遍历中的最后一个节点就是根节点
        int postorder_root_index=postorder_right;
        // 在中序遍历中定位根节点
        int inorder_root_index=index[postorder[postorder_root_index]];
        // 先把根节点建立出来
        TreeNode* root =new TreeNode(postorder[postorder_root_index]);
        // 得到左子树中的节点数目
        int size_left_subtree = inorder_root_index-inorder_left;
        // 递归地构造左子树,并连接到根节点
        // 后序遍历中「从 左边界 开始到 (左边界+size_left_subtree-1)个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
        root->left=myBuildTree(postorder,inorder,postorder_left,postorder_left+size_left_subtree-1,inorder_left,inorder_root_index-1);
        // 递归地构造右子树,并连接到根节点
        // 后序遍历中「从 (左边界+左子树节点数目) 开始到 根节点定位-1」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
        root->right=myBuildTree(postorder,inorder,postorder_left+size_left_subtree,postorder_root_index-1,inorder_root_index+1,inorder_right);
​
        return root;
        
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) 
    {
        int n = postorder.size();
​
        for(int i=0;i<n;i++)
        {
            index[inorder[i]]=i;
        }
        return myBuildTree(postorder,inorder,0,n-1,0,n-1);
    }
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值