题目描述
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。To do it!
示例1:
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
示例2:
输入: preorder = [-1], inorder = [-1]
输出: [-1]
限制: 0 <= 节点个数 <= 5000
题解
- 前序遍历的顺序为:先遍历根节点;随后递归地遍历左子树;最后递归地遍历右子树。
- 二叉树中序遍历的顺序为:先递归地遍历左子树;随后遍历根节点;最后递归地遍历右子树。
算法详解
分治递归
对于任意一棵树,
- 前序遍历的形式:
[根节点, [左子树的前序遍历结果], [右子树的前序遍历结果]]
; - 中序遍历的形式:
[[左子树的中序遍历结果], 根节点, [右子树的中序遍历结果]]
。
前序遍历的第一个节点就是根节点,只要在中序遍历中定位到根节点,就可以知道左右子树的节点数目。这样就知道了左右子树的前序遍历和中序遍历结果,得到与原问题相同的两个子问题,如此递归构造出左右子树,再接到根节点的左右位置。
在中序遍历中对根节点进行定位时,一种简单的方法是直接遍历整个中序遍历的结果并找出根节点,但这样做的时间复杂度较高,题解中使用哈希表来辅助定位,对中序遍历结果的节点值和索引建立映射,在构造二叉树之前,先对中序遍历的结果进行遍历,构造出哈希表,在此后构造二叉树的过程中,只需 O(1) 的时间即可定位到根节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| class Solution { unordered_map<int, int> inorderHash; TreeNode* buildTree(const vector<int>& preorder, const vector<int>& inorder, int pre_left, int pre_right, int in_left, int in_right) { if (pre_left > pre_right) return nullptr; TreeNode* root = new TreeNode(preorder[pre_left]); int root_idx_inorder = inorderHash[root->val]; int left_subtree_size = root_idx_inorder - in_left; root->left = buildTree(preorder, inorder, pre_left + 1, pre_left + left_subtree_size, in_left, root_idx_inorder - 1); root->right = buildTree(preorder, inorder, pre_left + left_subtree_size + 1, pre_right, root_idx_inorder + 1, in_right); return root; }
public: TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { int n = inorder.size(); for (int i = 0; i < n; ++i) inorderHash[inorder[i]] = i; return buildTree(preorder, inorder, 0, n - 1, 0, n - 1); } };
|
时间复杂度:O(n),空间复杂度:O(n)
其中 n 是树中的节点个数。除去返回答案需要的 O(n) 空间之外,需要使用 O(n) 的空间存储哈希映射,以及 O(h) (其中 h 是树的高度) 空间表示递归时的栈空间。这里 h <= n,所以总空间复杂度为 O(n)。
迭代法
迭代法的本质是:若这棵树只有左子树,相当于一条单链表,那前序遍历和中序遍历的结果是相反的顺序。利用栈来逆序存放前序遍历的结果,一旦遍历到最左下方,就开始弹出栈,同时开始扫描中序遍历的结果。
若过程中栈顶弹出的节点值和中序遍历从左到右遍历的节点值不相等,则说明至此不是单链表结构,而是多了个右孩子插在最后一个弹出节点的右边。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class Solution { public: TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { int n = preorder.size(); if (n == 0) return nullptr; TreeNode* root = new TreeNode(preorder[0]); stack<TreeNode*> st; st.push(root); int inorderIdx = 0; for (int i = 1; i < n; ++i) { TreeNode* node = st.top(); if (node->val != inorder[inorderIdx]) { node->left = new TreeNode(preorder[i]); st.push(node->left); } else { while (!st.empty() && st.top()->val == inorder[inorderIdx]) { node = st.top(); st.pop(); ++inorderIdx; } node->right = new TreeNode(preorder[i]); st.push(node->right); } } return root; } };
|
时间复杂度:O(n),空间复杂度:O(n)
其中 n 是树中的节点个数。除去返回答案需要的 O(n) 空间之外,还需要使用 O(h) (其中 h 是树的高度) 的空间来存储栈。这里 h <= n,所以(最坏情况下)总空间复杂度为 O(n)。