剑指 Offer 07. 重建二叉树 题解

142 阅读2分钟

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。

假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

样例:

给定:
前序遍历是:[3, 9, 20, 15, 7]
中序遍历是:[9, 3, 15, 20, 7]

返回:[3, 9, 20, null, null, 15, 7, null, null, null, null]
返回的二叉树如下所示:
    3
   / \
  9  20
    /  \
   15   7

思路

  • 通过前序遍历可以找到根节点,通过根节点在中序遍历中可以分出左右子树,并且知道左右子树各有多少个数
以样例为例,可以先从前序遍历中找到根节点 3, 然后 3把中序遍历分成[9]和[15, 20, 7]左右两个子树,之后再把这个规律推广
到更小的部份,如前序遍历中[20,15,7],可知根节点为20,左子树为[15],右子树为[20]

那么重点是:如何通过我们在中序遍历中已经得到的子树来确定他们在前序遍历中的位置?

我们可以先假设一下:假如二叉树一个有n个节点,根节点在第k个位置,那么

左子树在前序遍历的区间:[1, k + 1)
右子树在前序遍历中的区间:[k + 1, n - 1)

左子树在中序遍历中的区间:[0, k)
右子树在中序遍历中的区间:(k, n - 1]
PS:注意开闭区间

C++代码

/**
 * 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) {
        int n = preorder.size();
        for(int i = 0; i < n; i ++)
            hash[inorder[i]] = i;//使用hash表将数据存起来,在递归到每个节点时,在中序遍历中查找根节点位置的操作,只需要 O(1)O(1) 的时间。
        return dfs(preorder, inorder, 0, n - 1, 0, n - 1);//递归处理
    }

    TreeNode* dfs(vector<int>& pre, vector<int>& in, int pl, int pr, int il, int ir){
    //传入前序遍历与中序遍历,pl是前序遍历第一个节点下标,pr是前序遍历最后一个节点下标
    //il是中序遍历第一个节点下标,ir是中序遍历最后一个节点下标
        if(pl > pr) return NULL;
        int k = hash[pre[pl]] - il;//找根节点
        TreeNode* root = new TreeNode(pre[pl]);
        root->left = dfs(pre, in, pl + 1, pl + k, il, il + k - 1);//范围详解见后图片
        root->right = dfs(pre, in, pl + k + 1, pr, il + k + 1, ir);
        return root;
    }
};

屏幕截图 2022-03-28 003741.png

屏幕截图 2022-03-28 003758.png