题目描述
题目链接:leetcode 从前序与中序遍历序列构造二叉树
根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3 / \ 9 20 / \ 15 7
根据前序和中序遍历构造二叉树的方法
前序遍历:“根左右”(先访问根再访问左儿子最后访问右儿子)
中序遍历:“左根右”(先访问左儿子再访问根最后访问右儿子)
以下图的二叉树为例:
前序遍历为:FCADBEHGM
中序遍历为:ACBDFHEMG
首先要熟悉如何“手动”构造:
1.在中序中找到前序的第1个字母F:ACBDFHEMG。F左边的点ACBD必然在F的左子树上,F右边的点HEMG必然在右子树上。图示如下:
2.在中序中找到前序的第2个字母C,C在左子树ACBD上,C必然是左子树ACBD的根,C左边的点A必然在C的左子树上,C右边的点BD必然在右子树上。图示如下:
3.在中序中找到前序的第3个字母A,A的左右两边无字母,即A的左右子树为空。图示同上。
4.在中序中找到前序的第4个字母D,D左边的B必然在D的左子树上,D的右边无字母,即D的右子树为空。图示如下:
5.在中序中找到前序的第5个字母B,B的左右两边无字母,即B的左右子树为空。图示同上。
6.在中序中找到前序的第6个字母E:HEMG。E左边的点H必然在E的左子树上,E右边的点MG必然在E的右子树上。图示如下:
7.在中序中找到前序的第7个字母H,H的左右两边无字母,即H的左右子树为空。图示同上。
8.在中序中找到前序的第8个字母G,G左边的M必然在G的左子树上,G的右边无字母,即G的右子树为空。图示如下:
9.在中序中找到前序的第9个字母M,M的左右两边无字母,即M的左右子树为空。图示同上。
根据“手动”构造的步骤,很容易得到用程序构造的具体做法:
(1)遍历前序中的节点,选当前遍历到的节点作为“当前二叉树”的根,用“当前节点”将后序划分为左右子树两部分。
(2)分别在左右子树中重复(1)即可。
C++代码
class Solution {
public:
/**
dfs方法参数说明:
preorder:前序数组
inorder:后序数组
l:后序数组的区间左端点
r:后序数组的区间右端点
step:用于遍历前序数组的“当前节点”preorder[step]
非常关键的一点就是:
每次调用dfs,在后序中找到前序中的“当前节点”时,都要让step自增
因为只要将“当前节点”设为根,下一步就要遍历前序中的下一个节点
*/
TreeNode* dfs(vector<int>& preorder, vector<int>& inorder,int l,int r,int & step){
TreeNode *root=nullptr; //将当前子树的根设为空
for(int i=l;i<=r;i++){
if(inorder[i]==preorder[step]){ //如果在后序中找到前序中的“当前节点”
root=new TreeNode;
root->val=inorder[i]; //选“当前节点”作为“当前子树”的根
step++;
//递归构造左右子树
root->left=dfs(preorder,inorder,l,i-1,step);
root->right=dfs(preorder,inorder,i+1,r,step);
break;
}
}
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int l=0,r=preorder.size()-1,step=0;
return dfs(preorder,inorder,l,r,step);
}
};