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

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode 106. 从中序与后序遍历序列构造二叉树相关的知识,希望对你有一定的参考价值。

在这里插入图片描述
思路分析:解决二叉树的问题通常会用到分治思想,分治思想一般通过递归方法实现。

分治法的思想:把原问题分解(Divide)成若干个与原问题结构相同但规模更小的子问题,待子问题解决(Conquer)以后,再合并(Combine)它们,原问题就得以解决。

以题目中给出的例子为例,讲解如何构建二叉树。

中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]

在这里插入图片描述
leetcode 105. 从前序与中序遍历序列构造二叉树

方法一:在递归方法中,传入数组的拷贝(不推荐、复杂度较高)

  • 该方法在计算索引的时候会稍微容易一些。
class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) 
    {
        int inSize = inorder.size();
        int postSize = postorder.size();
       //当前为空节点
        if (inSize == 0)
            return NULL;
        //当前只有一个节点剩余---当前节点为叶子节点
        if (inSize == 1)
            return new TreeNode(inorder[0]);
        //给当前根节点赋值--当前根节点就是当前后序遍历数组最后一个元素
        TreeNode* root = new TreeNode(postorder[postSize - 1]);
        //找到当前中序遍历数组中根节点的下标位置
        int divPoint = 0;
        for (int i = 0; i < inSize; i++)
        {
            if (inorder[i] == postorder[postSize - 1])
            {
                divPoint = i;
                break;
            }
        }
        //构造一个数组包含当前中序遍历左半部分(左子树构成的左半部分)内容,对后序部分用以同样的操作
        vector<int> in1(inorder.begin(), inorder.begin() + divPoint);
        vector<int> pos1(postorder.begin(), postorder.begin() + divPoint);//这里都是加divpoint的原因是左子树整体长度一致


        //再构造两个数组分别包含中序和后序的右半部分
        vector<int> in2(inorder.begin() + divPoint+1,inorder.begin()+inSize);
        vector<int> pos2(postorder.begin() + divPoint,postorder.begin()+postSize-1);//注意这里后序遍历右半部分数组的构成,因为是左闭右开的区间

        root->left = buildTree(in1, pos1);//找到以当前左孩子为首的根节点,并返回----去当前根节点的左子树里面寻找
        root->right = buildTree(in2, pos2);//找到以当前右孩子为首的根节点,并返回-----去当前根节点的右子树里面寻找
        return root;
    }
};

在这里插入图片描述

方法二:在递归方法中,传入子数组的边界下标

注意:在递归方法中,有一个数组的边界下标。

  • 计算的依据是递归方法传入的中序遍历数组(的子数组)和后序遍历数组(的子数组)的长度相等。我的办法是解方程计算未知数,具体需要计算哪个参数我在下面的代码中已经注明了。
  • 下面展示了一个计算边界的方法。

在这里插入图片描述代码:

class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) 
    {
        int inSize = inorder.size();
        int postSize = postorder.size();
        return buildTree(inorder, postorder, 0, inSize - 1, 0, postSize - 1);
    }
    //inLeft:中序遍历当前数组起点  inRight:中序遍历当前数组终点   postLeft:后序遍历当前数组起点  postRight:后序遍历当前数组终点
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder, int inLeft, int inRight, int postLeft, int postRight)
    {
        //结束条件:指针错位----当前部分为空节点
        if (inLeft > inRight || postLeft > postRight)
            return NULL;
        //通过当前后序部分的终点下标来获取当前根节点的值
        int pivot=postorder[postRight]; 
        //把当前中序部分的起点的下标用临时变量保存
        int pivotIndex = inLeft;
        //通过起点不断后移,比对,找到根节点在中序数组的下标
        // 注意这里如果编写不当,有数组下标越界的风险
        while (inorder[pivotIndex] != pivot) { pivotIndex++; }
        //构造当前根节点
        TreeNode* root = new TreeNode(pivot);
        //给当前根节点左孩子赋值
        root->left = buildTree(inorder, postorder, inLeft,pivotIndex - 1, 
            postLeft, postRight - inRight + pivotIndex - 1);
        //给当前根节点的右孩子赋值
        root->right = buildTree(inorder, postorder, pivotIndex + 1, inRight,
            postRight - inRight + pivotIndex,postRight-1);
        //返回当前根节点
        return root;
    }
};

在这里插入图片描述

方法三:使用哈希表进行查找优化

  • 可以在递归构造前,把中序遍历的值和下标放在哈希表中就不需要通过遍历得到当前根结点在中序遍历中的位置了
class Solution {
    vector<int> postorder; //让 postorder 成为全局变量,以免在递归方法中一直传递
    unordered_map<int, int> map;
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) 
    {
        int inSize = inorder.size();
        int postSize = postorder.size();
        this->postorder = postorder;
        // 将节点值在inorder数组中的位置提前存入map
        for (int i = 0; i < inSize; i++)
            map[inorder[i]] = i;
        return buildTree(0, inSize - 1, 0, postSize - 1);
    }
    //inLeft:中序遍历当前数组起点  inRight:中序遍历当前数组终点   postLeft:后序遍历当前数组起点  postRight:后序遍历当前数组终点
    TreeNode* buildTree(int inLeft, int inRight, int postLeft, int postRight)
    {
        //结束条件:指针错位----当前部分为空节点
        if (inLeft > inRight || postLeft > postRight)
            return NULL;
        //构造当前根节点
        TreeNode* root = new TreeNode(postorder[postRight]);
        //获取根节点在当前中序数组中的下标位置
        int pivotIndex = map[postorder[postRight]];
        //给当前根节点左孩子赋值
        root->left = buildTree(inLeft,pivotIndex - 1, 
            postLeft, postRight - inRight + pivotIndex - 1);
        //给当前根节点的右孩子赋值
        root->right = buildTree(pivotIndex + 1, inRight,
            postRight - inRight + pivotIndex,postRight-1);
        //返回当前根节点
        return root;
    }
};

在这里插入图片描述

以上是关于leetcode 106. 从中序与后序遍历序列构造二叉树的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode(106):从中序与后序遍历序列构造二叉树

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

LeetCode Java刷题笔记—106. 从中序与后序遍历序列构造二叉树

LeetCode Java刷题笔记—106. 从中序与后序遍历序列构造二叉树

Leetcode No.106 从中序与后序遍历序列构造二叉树

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