题目链接:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
题目描述
根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
思路
首先要知道一个结论,前序/后序+中序序列可以唯一确定一棵二叉树,所以自然而然可以用来建树。
看一下前序和中序有什么特点,前序1,2,4,7,3,5,6,8
,中序4,7,2,1,5,3,8,6
;
有如下特征:
- 前序中左起第一位1肯定是根结点,我们可以据此找到中序中根结点的位置
inRoot
; - 中序中根结点左边就是左子树结点,右边就是右子树结点,即[左子树结点,根结点,右子树结点],我们就可以得出左子树结点个数为
intleftLen = inRoot - inLeft
;; - 前序中结点分布应该是:[根结点,左子树结点,右子树结点];
- 根据前一步确定的左子树个数,可以确定前序中左子树结点和右子树结点的范围;
- 如果我们要前序遍历生成二叉树的话,下一层递归应该是:
左子树:root->left = buildTreeCore(前序左子树范围,中序左子树范围,前序序列,中序序列);
;
右子树:root->right = buildTreeCore(前序右子树范围,中序右子树范围,前序序列,中序序列);
。 - 每一层递归都要返回当前根结点
root
;
时间复杂度:O(n)
空间复杂度:O(1)
代码
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.empty()||inorder.empty()||preorder.size() != preorder.size()) return nullptr;
int rootVal = preorder.front(), leftLen = 0;
while(inorder[leftLen]!= rootVal) ++ leftLen;
int rightLen = preorder.size() - leftLen -1;
TreeNode *root = new TreeNode(rootVal);
vector<int> preorderLeft(preorder.begin()+1, preorder.begin()+1+leftLen);
vector<int> preorderRight(preorder.end() - rightLen, preorder.end());
vector<int> inorderLeft(inorder.begin(), inorder.begin() + leftLen);
vector<int> inorderRight(inorder.end() - rightLen, inorder.end());
root->left = buildTree(preorderLeft, inorderLeft);
root->right = buildTree(preorderRight, inorderRight);
return root;
}
};
优化一下,只传preorder和inorder的引用,以及对应的坐标
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.empty()||inorder.empty()||preorder.size() != preorder.size()) return nullptr;
return buildTreeCore(0, inorder.size()-1, 0, inorder.size()-1, preorder, inorder);
}
TreeNode* buildTreeCore(int preLeft, int preRight, int inLeft, int inRight, vector<int>& preorder, vector<int>& inorder){
if(preLeft>preRight || inLeft>inRight) return nullptr;
TreeNode* root = new TreeNode(preorder[preLeft]); // 根节点
int inRoot = inLeft; // 中序遍历中根节点的位置
while (inRoot <= inRight && inorder[inRoot] != preorder[preLeft]) ++ inRoot;
int leftLen = inRoot - inLeft; // 左子树的长度,也是根节点在中序中的位置
root->left = buildTreeCore(preLeft+1, preLeft + leftLen, inLeft, inRoot-1, preorder,inorder);
root->right = buildTreeCore(preLeft+leftLen+1, preRight, inRoot+1, inRight, preorder,inorder);
return root;
}
};