leetcode 106 学习笔记(根据遍历构造二叉树)

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

问题描述:传送门

在这里插入图片描述

思路:

根据题意,流程如图:
在这里插入图片描述
这图就是先根据后续数组的最后一个元素为切割点,
去拆分中序数组。
再根据拆分后的中序数组,
反过来在切后序数组。

说到一层一层切割,想到了递归。

分以下几步:

第一步:如果数组大小为零,为空节点了。
第二步:如果不为空,取后序数组最后一个元素作为节点元素。
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点。
第四步:切割中序数组,切成中序左数组和中序右数组 。
第五步:切割后序数组,切成后序左数组和后序右数组。
第六步:递归处理左区间和右区间。

不难写出如下代码:(先把框架写出来)

 TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) {

        // 第一步
        if (postorder.size() == 0) 
        	return NULL;

        // 第二步:后序遍历数组最后一个元素,就是当前的中间节点
        int rootValue = postorder[postorder.size() - 1];
        TreeNode* root = new TreeNode(rootValue);

        // 叶子节点
        if (postorder.size() == 1) return root;

        // 第三步:找切割点
        int delimiterIndex;
        for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
            if (inorder[delimiterIndex] == rootValue)
             break;
        }

        // 第四步:切割中序数组,得到 中序左数组和中序右数组 
        // 第五步:切割后序数组,得到 后序左数组和后序右数组

        // 第六步
        root->left = traversal(中序左数组, 后序左数组);
        root->right = traversal(中序右数组, 后序右数组);

        return root;
    }

切割二叉树,并找到它的边界值是个难点。

此时要注意切割的标准,是左闭右开,还有左开右闭,还是左闭右闭。
在递归的过程中,这个标准不能变。

中序数组相对比较好切,找到切割点(后序数组的最后一个元素)在中序数组的位置,然后切割。

如下代码,以左闭右开为原则:

// 找到中序遍历的切割点
int delimiterIndex;
for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
    if (inorder[delimiterIndex] == rootValue) 
    	break;
}

// 左闭右开区间:[0, delimiterIndex)
vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
// [delimiterIndex + 1, end)
vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end() );

接下来切割后序数组,

首先后序数组的最后一个元素不能用,这是切割点 也是 当前二叉树中间节点的元素,已经用了。

有个细节,切割后的
中序数组大小一定是和后序数组的大小相同的。

中序数组我们都切成了左中序数组和右中序数组了,

去回顾一下图,
那么后序数组就可以按照左中序数组的大小来切割,
切成左后序数组和右后序数组。

代码如下:

// postorder 舍弃末尾元素,因为这个元素就是中间节点,已经用过了
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());

此时,中序数组切成了左中序数组和右中序数组,

后序数组切割成左后序数组和右后序数组。

接下来可以递归了,代码如下:

root->left = traversal(leftInorder, leftPostorder);
root->right = traversal(rightInorder, rightPostorder);

完整代码如下:

class Solution {
private:
    TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) {
        if (postorder.size() == 0) return NULL;

        // 后序遍历数组最后一个元素,就是当前的中间节点
        int rootValue = postorder[postorder.size() - 1];
        TreeNode* root = new TreeNode(rootValue);

        // 叶子节点
        if (postorder.size() == 1) 
        	return root;

        // 找到中序遍历的切割点
        int delimiterIndex;
        for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
            if (inorder[delimiterIndex] == rootValue) 
            	break;
        }
        // 切割中序数组
        // 左闭右开区间:[0, delimiterIndex)
        vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
        // [delimiterIndex + 1, end)
        vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end() );

        // postorder 舍弃末尾元素
        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());

        root->left = traversal(leftInorder, leftPostorder);
        root->right = traversal(rightInorder, rightPostorder);

        return root;
    }
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if (inorder.size() == 0 || postorder.size() == 0)
        	 return NULL;
        return traversal(inorder, postorder);
    }
};

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晓梦林

都看到这里了,支持一下作者呗~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值