题目描述
给定一棵二叉树的后序遍历和中序遍历,请复原出整棵二叉树。
注意:二叉树中没有值相同的节点。
样例
给定:
后序遍历是:[9,15,7,20,3]
中序遍历时:[9,3,15,20,7]
返回的二叉树如下所示:
3
/ \
9 20
/ \
15 7
算法
(递归) $O(n)$
这道题目和 Construct Binary Tree from Preorder and Inorder Traversal 类似。
递归建立整棵二叉树:先递归创建左右子树,然后创建根节点,并让指针指向两棵子树。
具体步骤如下:
- 先利用后序遍历找根节点:后序遍历的最后一个数,就是根节点的值;
- 在中序遍历中找到根节点的位置 $k$,则 $k$ 左边是左子树的中序遍历,右边是右子树的中序遍历;
- 假设左子树的中序遍历的长度是 $l$,则在后序遍历中,前 $l$ 个数就是左子树的后序遍历,接下来的数除了最后一个,就是右子树的后序遍历;
- 有了左右子树的后序遍历和中序遍历,我们可以先递归创建出左右子树,然后再创建根节点;
时间复杂度分析:我们在初始化时,用哈希表(unordered_map<int,int>
)记录每个值在中序遍历中的位置,这样我们在递归到每个节点时,在中序遍历中查找根节点位置的操作,只需要 $O(1)$ 的时间。此时,创建每个节点需要的时间是 $O(1)$,所以总时间复杂度是 $O(n)$。
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> pos;
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
int n = inorder.size();
for (int i = 0; i < n; i ++ )
pos[inorder[i]] = i;
return dfs(postorder, inorder, 0, n - 1, 0, n - 1);
}
// pl, pr 表示当前子树后序遍历在数组中的位置
// il, ir 表示当前子树中序遍历在数组中的位置
TreeNode* dfs(vector<int>&post, vector<int>&in, int pl, int pr, int il, int ir)
{
if (pl > pr) return NULL;
int k = pos[post[pr]] - il;
TreeNode* root = new TreeNode(post[pr]);
root->left = dfs(post, in, pl, pl + k - 1, il, il + k - 1);
root->right = dfs(post, in, pl + k, pr - 1, il + k + 1, ir);
return root;
}
};
如果给定的两个序列是错误的,并且要求当两个序列不能构成二叉树时,返回树根为null,或者作出其他标识,dfs中应该怎么判断呢?
比如 后根:BCA 中根:CAB 。
就是说对错误的输入序列,要能够识别出来。怎么处理呐?
如果存在值相同的结点,那么同样的算法应该就不适用了吧?构造的结果会不唯一
对滴,这样答案就不一定唯一了。
想请教一下,对这道题的复杂度分析,我的理解是否正确。
由于
dfs()
函数对于每个节点只会执行一次,且dfs()
函数内部的计算没有循环,是个常量,因此总时间复杂度是 $O(N)$。