Leetcode面经150题P105,P106——根据遍历序列构造二叉树

目录

一、必备知识

1、递归构造二叉树

2、如何根据遍历序列构造一棵二叉树

二、根据前序序列和中序序列构造代码实现

三、根据后序序列和中序序列构造代码实现

四、总结


一、必备知识

1、递归构造二叉树

1、树的构造和递归我们已经很清楚了,那么核心步骤有哪些呢?

一棵二叉树由什么组成?根节点+左子树+右子树

所以,在递归二叉树时我们同样遵循此步骤

①首先,确立根节点

②紧接着,继续通过递归的方式构造该根结点的左右子树

(注意,此处一定不要忘记将这两个子树连接到前面的根节点上去)

2、那么,这时候可能会想,递归的出口是什么呢?

首先要明确一点的是,构造二叉树的递归函数的返回值是一棵树结构

所以,当我们没有可以确立的根节点时,自然返回不了一棵树了,自然该递归函数也便就结束了

故递归函数的出口即当不能继续构造时,return nullptr;

(怎么判断能不能继续构造,在不同的题目中有不同的做法)

3、具体代码实现如下

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

TreeNode* build_tree(vector<int>& nodes, int& index) {
    if (index >= nodes.size() || nodes[index] == -1) {
        index++;
        return NULL;
    }
    
    TreeNode* root = new TreeNode(nodes[index]);
    index++;
    root->left = build_tree(nodes, index);
    root->right = build_tree(nodes, index);
    
    return root;
}

2、如何根据遍历序列构造一棵二叉树

1、在数据结构的学习中我们可以知道:

①根据前序序列和中序序列可以唯一确定一棵二叉树

>>Step1:通过前序序列可确定树的根结点的值(前序序列的第一个位置)

>>Step2:通过根的值在中序遍历序列中找到对应位置,便可唯一确定左子树和右子树

(此处找对应根的索引值有两种方法,第一种是每次都用一个for循环遍历中序序列,当找到对应的根值时返回根的索引值;第二种的效率更高,运用空间换时间的思想,通过建立一个哈希表,将索引和值对应起来,这样我们每次只用查找在哈希表中对应值的位置即可)

>>Step3:中序遍历中的左右子树确定了,前序遍历的左右子树自然便确定了

>>Step4:重复上面3个步骤,进一步寻找子树的根节点和左右子树(递归)

②根据后序序列和中序序列可以唯一确定一棵二叉树

(图来源:106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

>>Step1:通过后序序列可确定树的根结点的值(后序序列的最后一个位置)

>>Step2:通过根的值在中序遍历序列中找到对应位置,便可唯一确定左子树和右子树

>>Step3:中序遍历中的左右子树确定了,后序遍历的左右子树自然便确定了

>>Step4:重复上面3个步骤,进一步寻找子树的根节点和左右子树(递归)

通过上面的思想,我们可以知道递归构造二叉树的函数参数

1、前/后序序列(可以加const防止被更改)+中序序列

2、当前树在前/后序序列中的左边界+右边界

3、当前树在中序序列中的左边界+右边界

(因为两个序列一起才能唯一确定一棵树,所以在这两个序列中的位置都要包含)

具体代码实现如下:

二、根据前序序列和中序序列构造代码实现

/**
 * 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:
    //核心
    //1、确定根节点
    //2、划分子集

    //哈希表建立对应关系
    //在中序序列中建立哈希表
    //便于找到根节点在中序序列中的位置

    //但只适合“无重复结点值”的二叉树

    unordered_map<int,int>index;
   
    //递归参数的确定
    //分别结合先序序列和中序序列便可以确定一棵子树
    //故递归参数为子先序序列,子中序序列
    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=preorder_left;
        //在中序遍历中定位根节点
        int inorder_root=index[preorder[preorder_root]];
        //建立根节点
        TreeNode* root=new TreeNode(preorder[preorder_root]);

        //第二步:建立子树
        //得到左子树的节点数目
        int size_left_subtree=inorder_root-inorder_left;
        //递归构造时分别传入新的先序和中序序列
        //递归构造左子树
        root->left=myBuildTree(preorder,inorder,preorder_left+1,preorder_left+size_left_subtree,inorder_left,inorder_root-1);
        //递归构造右子树
        root->right=myBuildTree(preorder,inorder,preorder_left+size_left_subtree+1,preorder_right,inorder_root+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);
    }
};

三、根据后序序列和中序序列构造代码实现

/**
 * 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>&inorder,const vector<int>&postorder,int inorder_left,int inorder_right,int postorder_left,int postorder_right)
    {
        //递归出口
        if(postorder_right<postorder_left)
        {
            return nullptr;//无法继续构造子树
        }
        //根据后序序列确定根结点的位置
        int postorder_index=postorder_right;
        //对应到中序序列中
        int inorder_root=index[postorder[postorder_index]];
        //开始构造树
        //构造根结点
        TreeNode* root=new TreeNode(inorder[inorder_root]);
        //构造子树
        //获取左子树的大小
        int size_left_subtree=inorder_root-inorder_left;
        //递归构造左子树,一定要连接到根上
        root->left=MybuildTree(inorder,postorder,inorder_left,inorder_root-1,postorder_left,postorder_left+size_left_subtree-1);
        //递归构造右子树
        root->right=MybuildTree(inorder,postorder,inorder_root+1,inorder_right,postorder_left+size_left_subtree,postorder_right-1);
        //构造完毕
        return root;
    }

    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        int len=inorder.size();
        //建立中序序列中根的关系
        for(int i=0;i<len;i++)
        {
            index[inorder[i]]=i;
        }
        //返回递归构造的结果
        return MybuildTree(inorder,postorder,0,len-1,0,len-1);
    }
};

看完这两个可以举一反三一下另一种不能唯一确定的结构

889. 根据前序和后序遍历构造二叉树 - 力扣(LeetCode)

四、总结

      本文详细介绍了如何通过遍历序列构造一棵二叉树的思想和代码,核心还是离不开递归和树的划分,递归是贯穿整个树的思想,一定要多加练习。

  • 28
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值