剑指Offer 07 重建二叉树
参考学习题解:
剑指 Offer 07. 重建二叉树(分治算法,清晰图解)作者:Krahets
解题思路:
前序遍历性质: 节点按照 [ 根节点 | 左子树 | 右子树 ] 排序。
中序遍历性质: 节点按照 [ 左子树 | 根节点 | 右子树 ] 排序。
以题目示例为例:
前序遍历划分 [ 3 | 9 | 20 15 7 ]
中序遍历划分 [ 9 | 3 | 15 20 7 ]
根据以上性质,可得出以下推论:
- 前序遍历的首元素 为 树的根节点 node 的值。
- 在中序遍历中搜索根节点 node 的索引 ,可将 中序遍历 划分为 [ 左子树 | 根节点 | 右子树 ] 。
- 根据中序遍历中的左(右)子树的节点数量,可将 前序遍历 划分为 [ 根节点 | 左子树 | 右子树 ] 。
作者:jyd
链接:https://leetcode.cn/problems/zhong-jian-er-cha-shu-lcof/solution/mian-shi-ti-07-zhong-jian-er-cha-shu-di-gui-fa-qin/
来源:力扣(LeetCode)
为了提升效率,本文使用哈希表 dic 存储中序遍历的值与索引的映射,查找操作的时间复杂度为
O(1) ;
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: unordered_map<int,int> map; TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { // 将中序序列用哈希表存储,便于查找根节点 for(int i = 0;i < inorder.size();i++) map[inorder[i]] = i; // 传入参数:前序,中序,前序序列根节点,中序序列左边界,中序序列右边界 return build(preorder,inorder,0,0,inorder.size()-1); } TreeNode* build(vector<int>& preorder, vector<int>& inorder,int pre_root,int in_left,int in_right){ if(in_left > in_right) return NULL; TreeNode* root = new TreeNode(preorder[pre_root]); // 根节点在中序序列中的位置,用于划分左右子树的边界 int in_root = map[preorder[pre_root]]; // 左子树在前序中的根节点位于:pre_root+1,左子树在中序中的边界:[in_left,in_root-1] root->left = build(preorder,inorder,pre_root+1,in_left,in_root-1); // 右子树在前序中的根节点位于:根节点+左子树长度+1 = pre_root+in_root-in_left+1 // 右子树在中序中的边界:[in_root+1,in_right] root->right = build(preorder,inorder,pre_root+in_root-in_left+1,in_root+1,in_right); return root; } };
####Q:右子树在前序中的根节点位于:根节点+左子树长度+1 = pre_root+in_root-in_left+1 这个是怎么算的。 左子树的长度应该是多少
A:
右子树在前序中的根节点下标 = 根节点下标 + 左子树长度 + 1
其中,根节点下标是 pre_root,左子树的长度可以在中序遍历中计算得到,即 in_root - in_left。
所以,右子树在前序遍历中的根节点下标可以计算为:
pre_root + in_root - in_left + 1
左子树的长度应该是 in_root - in_left,因为在中序遍历中,根节点左边的元素都属于左子树,右边的元素都属于右子树。所以,根据根节点在中序遍历中的下标 in_root 和左边界 in_left 的差值,即可得到左子树的长度。