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

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

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

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

例如,给出

中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

思路:依据中序与后序遍历序列构造二叉树是数据结构里的经典问题,类似的还有依据先序和中序遍历序列构建二叉树,思考过程类似,前者的思路为:后序遍历找到根节点, 中序遍历定左右的原则,后者的思路为:先序遍历找到根节点,中序遍历定左右的原则下面详细说明如何构建二叉树。

先明确中序遍历的顺序为:左子树,根节点,右子树,而后序遍历顺序为:左子树,右子树,根节点。按照示例有inorder = [9,3,15,20,7]postorder = [9,15,7,20,3],从后序遍历的顺序可以发现,根节点一定是最后被访问的,所以,根节点一定在后序遍历序列的最末尾的位置,假设使用变量idx指示根节点在后序序列的下标,此时idx=4,也就是posterder[idx] = 3为根节点,从后序序列中得到了根节点,我们再在中序遍历序列中找到根节点3,可以看到以3的位置将中序遍历序列分为了两部分,左边为[9],右边为[15,20,7],由中序遍历的顺序可以得知,当在中序序列中确定根节点后,位于根节点左侧的所有元素都位于根节点的左子树,位于根节点右侧的所有元素都位于根节点的右子树

在这里插入图片描述

得到左右子树包含的元素后,我们可以递归进入左右子树构建左右子树。下面讨论关于递归的细节,因为我们依靠的是后序遍历找到根节点,中序遍历定左右的原则,当找到当前根节点后,idx应该改变以指向下一个根节点,这样递归构建子树的时候才可以轻松得知根节点。注意到后序遍历序列顺序为:左,右,根,如果idx--,那么idx指向的位置有什么含义呢?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dMG56FiM-1601022708939)(assets/image-20200925160012754.png)]

我们可以发现idx指向的位置已经进入右子树,右子树依然按照后序遍历的顺序:左、右、根,根节点最后被遍历,所以我们可以断定,此时idx指向了右子树的根节点,所以当我们递归下去的时候又可以继续按照之前的步骤,在中序遍历序列中找到右子树根节点,位于右子树根节点左边的为其左子树,位于右子树根节点右边的为其右子树,递归处理。

在这里插入图片描述

从上图中也可以发现节点20的左子树只有一个元素15,右子树也只有一个元素7,可以不比再继续递归,所以此时发现一个递归出口为当区间被划分到只剩下一个元素时,创建该元素,同时按照idx的语义执行idx--,然后返回即可。下面看代码实现。

代码实现的一些细节,首先我们使用下标lr来表示我们在inorder的范围,在该范围内构建树。上面提到需要查找根节点在中序遍历序列中的位置,为了快速查找,可以使用哈希表存储中序遍历序列,根据后序遍历序列确定根节点的值,快速在哈希表中查找到根节点在中序遍历序列中的下标,假设根节点在中序遍历序列中的下标为x。则左子树为inorder[l, x-1],右子树为inorder[x+1, r]

class Solution {
public:
    unordered_map<int, int> hash;//哈希表存储中序遍历序列,key为节点值,val为key在inorder数组的下标。方便快速查找根节点
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        //由后序遍历的特性得出,后序遍历序列的最后元素即为当前根节点,idx始终指向根节点
        int idx = postorder.size()-1;
        for (int i = 0; i < inorder.size(); i++) hash[inorder[i]] = i;//构建哈希表
        return build(inorder, postorder, 0, inorder.size()-1, idx);
    }

    TreeNode* build(vector<int>& inorder, vector<int>& postorder, int l, int r, int& idx) {
        if (l > r) return NULL;
        if (l == r) {	//区间内只有一个元素时,创建该元素,并返回该元素
            idx--;		//创建了一个元素后,根节点发生变化
            return new TreeNode(inorder[l]);
        }
        int root = postorder[idx--];//根节点为后序遍历序列的最后一个元素
        int x = hash[root];			//得到根节点在中序遍历序列中的位置
        TreeNode* res = new TreeNode(root);//依据根节点创建节点
        res->right = build(inorder, postorder, x + 1, r, idx);//后序序列为:左右根,因先递归创建右子树
        res->left = build(inorder, postorder, l, x - 1, idx);//递归创建左子树
        return res;
    }
};




为了方便,给出从中序遍历序列和先序遍历序列构造二叉树的代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    unordered_map<int, int> hash;
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        for (int i = 0; i < preorder.size(); i++) hash[inorder[i]] = i;
        int idx = 0;
        return build(preorder, inorder, 0, inorder.size()-1, idx);
    }

    TreeNode* build(vector<int>& preorder, vector<int>& inorder, int l, int r, int& idx) {
        if (l > r) return NULL;
        if (l == r) {
            idx++;
            return new TreeNode(inorder[l]);
        }
        int rootVal = preorder[idx++];
        TreeNode* root = new TreeNode(rootVal);
        int x = hash[rootVal];
        root->left = build(preorder, inorder, l, x - 1, idx);
        root->right = build(preorder, inorder, x + 1, r, idx);
        return root;
    }
};
  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值