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

1 问题

根据一棵树的中序遍历与后序遍历构造二叉树。

注意: 你可以假设树中没有重复的元素。

例如,给出

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

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

返回如下的二叉树:
在这里插入图片描述

2 解法

首先确定如何根据中序与后序数组构造一个唯一的二叉树:
(1)每次后序数组的最后一个元素即为根节点。
(2)根据根节点将中序数组切分成左、右两部分。
(3)根据中序数组的左、右两部分切割后序数组。
(4)此时左部分属于左子树,右部分属于右子树。
(5)新的左部分/右部分的后续数组中最后一个元素为根节点。
(6)重复上述步骤。
在这里插入图片描述

/**
 * 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* traversal(vector<int>& inorder, vector<int>& postorder)
    {
        if(postorder.size() == 0)    return NULL;
        //1.后序数组的最后一个元素即为根节点
        int rootVal = postorder[postorder.size() - 1];
        TreeNode* root = new TreeNode(rootVal);
        // 递归结束条件,后序数组中仅剩一个元素,则该节点为叶子节点,遍历完成
        if(postorder.size() == 1)
            return root;
        //2.根据根节点值,切割中序数组
        //2.1 找到根节点在中序数组中的索引
        int delimiterIndex; //中序数组的分割点索引
        for(delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++)
        {
            if(inorder[delimiterIndex] == rootVal)
                break; 
        }
        //2.2 切割中序数组,区间统一左闭右开
        //左中序[0, delimiterIndex)
        vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
        //右中序[delimiterIndex + 1, end), 加1是为了去除根节点
        vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end());
        //3. 切割后序数组,使用左中序数组大小作为切割点
        //去除根节点
        postorder.resize(postorder.size() - 1);
        //左后序[0, leftInorder.size())
        vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
        //右后序[leftInorder.size(), end)
        vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());
        //4.递归
        //左中序与左后序
        root->left = traversal(leftInorder, leftPostorder);
        //右中序与右后序
        root->right = traversal(rightInorder, rightPostorder);
        
        //返回根节点
        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if(inorder.size() == 0 || postorder.size() == 0)
            return NULL;
        return traversal(inorder, postorder);
    }
};

由于上述代码定义了多个vector,造成了资源开销过大,可以采用改变索引的方式,在同一个数组进行切片

/**
 * 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* traversal(vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& postorder, int postorderBegin, int postorderEnd)
    {
        //后序数组区间[postorderBegin,postorderEnd)
        if(postorderBegin == postorderEnd)
            return nullptr;
        //1.找分割点,后序数组最后一个元素为根节点(分割点)
        int rootVal = postorder[postorderEnd - 1];
        //构造根节点
        TreeNode* root = new TreeNode(rootVal);
        //2.根据分割点切割中序数组
        //2.1.找到分割点在中序数组中的索引
        int delimiterIndex;
        for(delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++)
        {
            if(inorder[delimiterIndex] == rootVal)
                break;
        }
        //2.2.根据分割点切割中序数组
        //左中序区间[inorderBegin, delimiterIndex)
        int leftInorderBegin = inorderBegin;
        int leftInorderEnd = delimiterIndex; 
        //右中序区间[delimiterIndex + 1, inorderEnd)
        int rightInorderBegin = delimiterIndex + 1;
        int rightInorderEnd = inorderEnd;
        
        //3.根据左中序数组长度切分后序数组
        //左后序区间[postorderBegin, postorderBegin + (delimiterIndex - inorderBegin))
        int leftPostorderBegin = postorderBegin;
        int leftPostorderEnd = postorderBegin + (delimiterIndex - inorderBegin);
        //右后序区间[postorderBegin + (delimiterIndex - inorderBegin), postorderEnd - 1)
        int rightPostorderBegin = postorderBegin + (delimiterIndex - inorderBegin);
        int rightPostorderEnd = postorderEnd - 1;  //去掉根节点
        
        //4.递归
        root->left = traversal(inorder, leftInorderBegin, leftInorderEnd, postorder, leftPostorderBegin, leftPostorderEnd);
        root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, postorder, rightPostorderBegin, rightPostorderEnd);
        
        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if(inorder.size() == 0 || postorder.size() == 0)
            return NULL;
        return traversal(inorder, 0, inorder.size(), postorder, 0, postorder.size());
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 利用二叉树的中和后可以确定该二叉树的先序列。具体方法如下: 1. 后的最后一个节点一定是根节点,将其记录下来。 2. 在中中找到根节点的位置,将中分成左子树和右子树两部分。 3. 根据左子树和右子树的节点数量,将后分成左子树和右子树两部分。 4. 递归处理左子树和右子树,得到左子树和右子树的先序列。 5. 将根节点和左子树的先序列、右子树的先序列拼接起来,得到整个二叉树的先序列。 需要注意的是,在递归处理左子树和右子树时,需要根据左子树和右子树的节点数量来确定后的左子树和右子树的范围。如果左子树或右子树为空,那么相应的先序列也为空。 ### 回答2: 首先,我们需要了解二叉树的遍方式。二叉树的遍方式有三种:先、中和后。先是从根节点开始,先遍左子树,再遍右子树。中是先遍左子树,再遍根节点,最后遍右子树。后是先遍左子树,再遍右子树,最后遍根节点。 假设现在我们有一个二叉树,该二叉树的中为:DBEAFC,后为:DEBFCA。现在我们需要确定该二叉树的先序列。我们可以利用以下方法确定: 1.找出后序列的最后一个节点,即根节点,该节点是先序列的第一个节点。 2.在中序列中找出根节点的位置,将中序列分为左子树和右子树两个部分。 3.分别对左子树和右子树进行遍,得到左子树的先序列和右子树的先序列。 4.将左子树的先序列和右子树的先序列依次拼接在根节点之后,即得到该二叉树的先序列。 按照上述方法,我们可以得到该二叉树的先序列为:ABDECF。具体步骤如下: 1.后序列为DEBFCA,最后一个节点为A,因此A是先序列的第一个节点。 2.在中序列中找到A的位置,将中序列分为左子树DBE和右子树FC两个部分。 3.左子树中的节点顺为DBE,其先序列为:D、B、E;右子树中的节点顺为FC,其先序列为:F、C。 4.将上述两个先序列依次拼接在A之后,即可得到该二叉树的先序列为:ABDECF。 ### 回答3: 二叉树、中、后是二叉树中最基本也是最常见的三种遍方式,它们分别表示对树中所有节点的遍方式,它们的命名方式取决于遍根节点的顺。如果我们只知道一颗二叉树的中和后,但不知道其前,可以通过一定的方法推导出其前。 我们通过画图来模拟一下怎样根据中和后确定二叉树的前。举个例子,我们假设现在已知二叉树的中序列为:F H J K T X U N L O G A M P S Q,后序列为:F J K H X N O L U G M Q S P A T。首先我们需要定位中序列中根节点的位置,这里根节点是T。然后我们根据后序列,将其最后一个节点T定位为根节点,随后按照根节点分割左右子树的方法,将后序列分为两部分:F H J K X U N L O G A M P S Q | T,其中 | 表示根节点。接下来我们用同样的方法来处理中序列,将其按照根节点T的位置分为两部分:F H J K | T | X U N L O G A M P S Q。根据中我们可以推得左子树的节点序列为F H J K,右子树的节点序列为X U N L O G A M P S Q。接下来我们可以继续重复上述过程去构造左右子树的前序列,得到左子树的前序列为:T H F J K X N O L U G M Q S P A,右子树的前序列为:T X U N L O G A M P S Q。最后,我们可以将左右子树的前序列合并在一起,再加上根节点T,即可得到二叉树的前序列:T H F J K X N O L U G M Q S P A T。 总的来说,通过二叉树的中和后,我们可以确定二叉树的一些信息,例如二叉树的叶子节点、节点数、深度,以及可以通过这个方式推导出二叉树的前序列。当然,如果我们已知二叉树的前序列和中序列,我们同样可以通过类似的方式推导出其后序列

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值