LeetCode题解 二叉树(九):106 中序和后序遍历序列构造二叉树;105 从前序与中序遍历序列构造二叉树

下面要讲的两道题,从二叉树的角度来讲,是非常重要的,此前一直是遍历二叉树,现在就要根据数组,构造二叉树

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

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

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

构造思路也不复杂,根据后序的最后一个结点(必是中间节点)为切割点,切割中序数组,再反过来切割后序数组,使用上述例子来说明该过程,见下图:

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

因为后序的遍历顺序为左右中,所以最后一个结点必然是根节点3,然后根据根节点切割中序序列为根结点左子树9和根结点右子树15 20 7

左子树就一个结点,后序的最后一个结点就是它本身,左子树至此构造完毕

右子树的最后一个结点为20,将中序序列分割为左子树15和右子树7

使用递归来一层一层切割是比较容易想到的。

递归函数的返回值应当是结点,因为最终要返回二叉树的根节点

传入参数则是中序和后序的序列,或者是索引值,随想录中给出了两个版本的代码

终止条件也很简单,当数组大小为零时,说明没有结点可以用于构造

若不为零,就取后序数组的最后一个元素作为根结点,以此作为中序序列的切割点(从中序序列中找到相同的值就行),将中序序列风格为左子树和右子树,以此将后序数组分割为左右子树(左右子树的内容都一样,只是索引值不同)

然后按照这个顺序,一步一步递归,构造出目标二叉树

其中最麻烦的一步应该是切割后序数组,方法是从后序的剩余数组中,按照中序左右子树的大小来切割就行

下面给出传入参数为数组的代码:

TreeNode* reversal(vector<int>& inorder, vector<int>& postorder) {
    if (postorder.size() == 0) return nullptr;
    int rootValue = postorder[postorder.size() - 1];
    TreeNode* root = new TreeNode(rootValue);

    if (postorder.size() == 1) return root;
    // 找到中序序列的切割点
    int cutPoint;
    for (cutPoint = 0; cutPoint < inorder.size(); cutPoint++) {
        if (inorder[cutPoint] == rootValue) break;
    }
    // 切割中序序列
    vector<int> leftInOrder(inorder.begin(), inorder.begin() + cutPoint);
    vector<int> rightInOrder(inorder.begin() + cutPoint + 1, inorder.end());

    postorder.resize(postorder.size() - 1);

    // 切割后序序列
    vector<int> leftPostOrder(postorder.begin(), postorder.begin() + leftInOrder.size());
    vector<int> rightPostOrder(postorder.begin() + leftInOrder.size(), postorder.end());

    root->left = reversal(leftInOrder, leftPostOrder);
    root->right = reversal(rightInOrder, rightPostOrder);

    return root;
}

TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
    if (inorder.size() == 0 || postorder.size() == 0) return nullptr;

    return reversal(inorder, postorder);
}

借用随想录中的说法:

以上版本的代码,性能并非很好,因为每一次递归都要构造vector,耗时又耗空间,但确实最容易理解的

接下来就是参入参数为数组索引版的代码:

// 中序区间:[inorderBegin, inorderEnd),后序区间[postorderBegin, postorderEnd)
TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& postorder, int postorderBegin, int postorderEnd) {
    if (postorderBegin == postorderEnd) return NULL;

    int rootValue = postorder[postorderEnd - 1];
    TreeNode* root = new TreeNode(rootValue);

    if (postorderEnd - postorderBegin == 1) return root;

    int delimiterIndex;
    for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
        if (inorder[delimiterIndex] == rootValue) break;
    }
    // 切割中序数组
    // 左中序区间,左闭右开[leftInorderBegin, leftInorderEnd)
    int leftInorderBegin = inorderBegin;
    int leftInorderEnd = delimiterIndex;
    // 右中序区间,左闭右开[rightInorderBegin, rightInorderEnd)
    int rightInorderBegin = delimiterIndex + 1;
    int rightInorderEnd = inorderEnd;

    // 切割后序数组
    // 左后序区间,左闭右开[leftPostorderBegin, leftPostorderEnd)
    int leftPostorderBegin =  postorderBegin;
    int leftPostorderEnd = postorderBegin + delimiterIndex - inorderBegin; // 终止位置是 需要加上 中序区间的大小size
    // 右后序区间,左闭右开[rightPostorderBegin, rightPostorderEnd)
    int rightPostorderBegin = postorderBegin + (delimiterIndex - inorderBegin);
    int rightPostorderEnd = postorderEnd - 1; // 排除最后一个元素,已经作为节点了

    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());
}
105 从前序与中序遍历序列构造二叉树 medium

思想与上一题近似,给出示例:

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

105. 从前序与中序遍历序列构造二叉树

区别就在于,我们此前使用后序的最后一个结点当做中序的切割点

这道题我们直接用数组索引版本

TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& preorder, int preorderBegin, int preorderEnd) {
    if (preorderBegin == preorderEnd) return NULL;

    int rootValue = preorder[preorderBegin]; // 注意用preorderBegin 不要用0
    TreeNode* root = new TreeNode(rootValue);

    if (preorderEnd - preorderBegin == 1) return root;

    int delimiterIndex;
    for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
        if (inorder[delimiterIndex] == rootValue) break;
    }
    // 切割中序数组
    // 中序左区间,左闭右开[leftInorderBegin, leftInorderEnd)
    int leftInorderBegin = inorderBegin;
    int leftInorderEnd = delimiterIndex;
    // 中序右区间,左闭右开[rightInorderBegin, rightInorderEnd)
    int rightInorderBegin = delimiterIndex + 1;
    int rightInorderEnd = inorderEnd;

    // 切割前序数组
    // 前序左区间,左闭右开[leftPreorderBegin, leftPreorderEnd)
    int leftPreorderBegin =  preorderBegin + 1;
    int leftPreorderEnd = preorderBegin + 1 + delimiterIndex - inorderBegin; // 终止位置是起始位置加上中序左区间的大小size
    // 前序右区间, 左闭右开[rightPreorderBegin, rightPreorderEnd)
    int rightPreorderBegin = preorderBegin + 1 + (delimiterIndex - inorderBegin);
    int rightPreorderEnd = preorderEnd;

    root->left = traversal(inorder, leftInorderBegin, leftInorderEnd,  preorder, leftPreorderBegin, leftPreorderEnd);
    root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, preorder, rightPreorderBegin, rightPreorderEnd);

    return root;
}

TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
    if (inorder.size() == 0 || preorder.size() == 0) return NULL;

    // 参数坚持左闭右开的原则
    return traversal(inorder, 0, inorder.size(), preorder, 0, preorder.size());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值