给定两个整数数组 preorder
和 inorder
,其中 preorder
是二叉树的先序遍历, inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例 1:
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] 输出: [3,9,20,null,null,15,7]
示例 2:
输入: preorder = [-1], inorder = [-1] 输出: [-1]
解题思路:
前序遍历性质: 节点按照 [ 根节点 | 左子树 | 右子树 ] 排序。
中序遍历性质: 节点按照 [ 左子树 | 根节点 | 右子树 ] 排序。
以题目示例为例:
前序遍历划分 [ 3 | 9 | 20 15 7 ]
中序遍历划分 [ 9 | 3 | 15 20 7 ]
根据以上性质,可得出以下推论:
前序遍历的首元素 为 树的根节点 node 的值。
在中序遍历中搜索根节点 node 的索引 ,可将 中序遍历 划分为 [ 左子树 | 根节点 | 右子树 ] 。
根据中序遍历中的左(右)子树的节点数量,可将 前序遍历 划分为 [ 根节点 | 左子树 | 右子树 ] 。
通过以上三步,可确定 三个节点 :1.树的根节点、2.左子树根节点、3.右子树根节点。
根据分治思想,对于树的左、右子树,仍可复用以上方法划分子树的左右子树。
算法流程:
递推参数: 根节点在前序遍历的索引 root 、子树在中序遍历的左边界 left 、子树在中序遍历的右边界 right 。
终止条件: 当 left > right ,代表已经越过叶节点,此时返回 null 。
递推工作:
建立根节点 node : 节点值为 preorder[root] 。
划分左右子树: 查找根节点在中序遍历 inorder 中的索引 i 。、
构建左右子树: 开启左右子树递归。
根节点索引 中序遍历左边界 中序遍历右边界
左子树 root + 1 left i - 1
右子树 i - left + root + 1 i + 1 right
> **TIPS:** `i - left + root + 1`含义为 `根节点索引 + 左子树长度 + 1`
返回值: 回溯返回 node ,作为上一层递归中根节点的左 / 右子节点。
/**
* 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* buildTree(vector<int>& preorder, vector<int>& inorder) {
this -> preorder = preorder;
for(int i = 0; i < inorder.size();i++)
dic[inorder[i]] = i;
return recur(0,0,inorder.size() - 1);
}
private:
vector<int> preorder;
unordered_map<int,int> dic;
TreeNode * recur(int root ,int left, int right)
{
if(left > right)
{
return nullptr;
}
TreeNode* node = new TreeNode(preorder[root]);
int i = dic[preorder[root]];
node -> left = recur(root + 1, left, i- 1);
node -> right = recur(root + i - left + 1,i + 1,right);
return node;
}
};