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

题目描述

根据一棵树的前序遍历与中序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

1
2
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]

返回如下的二叉树:

1
2
3
4
5
  3
/ \
9 20
/ \
15 7

解题思路

这道题我们可以运用递归写出非常简洁和通俗易懂的代码,需要注意递归时函数参数的设定。

在求解的过程中,我们主要利用了前序遍历和中序遍历的性质:

对于任意一颗树而言,前序遍历的形式总是:

[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]

根节点总是前序遍历中的第一个节点。因此,我们总是可以通过前序遍历找到根节点。

而中序遍历的形式总是:

[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]

可以看到的是,在中序遍历序列中,在根节点左端的都是左子树的中序遍历结果,在根节点右端的都是右子树的中序遍历结果。只要我们在中序遍历中找到根节点的位置,那么我们就可以确定左子树和右子树的结点数目。

因此,我们先可以通过先序遍历找出根节点,然后再通过中序遍历来确定左子树的结点数目。这样一来,我们就确定了一棵树的根节点,左子树的结点数目和右子树的结点数目。接着递归地构造左子树和右子树直到递归出口——当前序遍历或者中序遍历的数组为空时,返回nullptr指针。这样就最终构建了整颗树。

示例代码如下。

示例代码

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
33
34
35
36
37
38
39
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

unordered_map<int,int>index;

TreeNode* myBuildTree(vector<int>& preorder, vector<int>& inorder,int preorder_left,int preorder_right,int inorder_left,int inorder_right)
{
//递归出口
if(preorder_left>preorder_right) return nullptr;
//前序遍历的左端点就是根节点,找出根节点的值
int preorder_root=preorder[preorder_left];
//运用map来加速找到根节点在中序遍历中的下标
int inorder_root_index=index[preorder_root];
//算出左子树的结点个数
int size_left_subtree=inorder_root_index-inorder_left;
//创建根节点
TreeNode* root=new TreeNode(preorder_root);

//递归创建左右结点
root->left=myBuildTree(preorder,inorder,preorder_left+1,preorder_left+size_left_subtree,inorder_left,inorder_root_index-1);
root->right=myBuildTree(preorder,inorder,preorder_left+size_left_subtree+1,preorder_right,inorder_root_index+1,inorder_right);

return root;
}

TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
{
for(int i=0;i<inorder.size();++i)
{
index[inorder[i]]=i;
}
return myBuildTree(preorder,inorder,0,preorder.size()-1,0,inorder.size()-1);
}