1.题目
给定一棵树的前序遍历 preorder 与中序遍历 inorder。请构造二叉树并返回其根节点
示例1
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]
示例2
Input: preorder = [-1], inorder = [-1]
Output: [-1]
2.思路
知识回顾:前序遍历规律:中左右;中序遍历规律:左中右
该答案返回的是二叉树的根节点,这要求我们在计算过程中要构建这个二叉树
二叉树最重要的就是找到根节点,而根据前序遍历的规则,因此前序遍历序列的第一个值就是该二叉树根节点的值,再去中序遍历序列中寻找可以很容易的知道该根节点的左子树和右子树。根据二叉树的特性,最终一定可以到达二叉树的叶子节点。
因此此题中我们最重要的就是找到一个节点的左右节点并进行递归的找到上一个节点的左右节点,直到构建整个二叉树,从而返回根节点。
具体过程如下:
- 先找到中序遍历序列中的根节点的位置ind,从而判断了在中序遍历中根节点的左子树序列(lz, ind - 1)和右子树序列(ind + 1, rz)。
- 接着找到前序遍历中根节点的左子树序列(lq + 1, ind - lz + lq)和右子树序列(ind - lz + lq + 1, rq)。
- 把前序遍历的根节点的左子树和后序遍历的根节点的左子树进行递归,把前序遍历的根节点的右子树和后序遍历的根节点的右子树进行递归。
- 从上到下依次递归,直到找到叶子节点,return nullptr。
- 递归返回,用叶子的上一层节点指针指向叶子节点,不断返回上一层更新节点的左子树和右子树,最终返回到根节点
时间复杂度分析:O(n * m),n是节点的数量,m是在中序遍历中寻找根节点或者节点的for循环
3.c++代码
/**
* Definition for a binary tree node.
* 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) {}
* };
*/
class Solution {
public:
TreeNode* func(vector<int> preorder, int lq, int rq, vector<int> inorder, int lz, int rz) {
if (lq > rq) {
return nullptr;
}
TreeNode *p = new TreeNode(preorder[lq]);
int ind;
for (int i = 0; i <= rz; i++) {
if (inorder[i] == preorder[lq]) {
ind = i;
break;
}
}
p->left = func(preorder, lq + 1, ind - lz + lq, inorder, lz, ind - 1);
p->right = func(preorder, ind - lz + lq + 1, rq, inorder, ind + 1, rz);
return p;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return func(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1);
}
};