先说好,我不喜欢极致简洁的代码,不欣赏只追求简洁而忽略可读性的做法(我指的不是我超级详细的注释哈😹)
这里只给出了105题的代码,106也是差不多的。
构建思路 : (最好自己拿着例子跟着划一下)
因为前序的第一个位置的结点是根节点,通过前序找到根节点的值后,利用根节点的值去中序找到其所在下标来切割中序,再反过来拿中序切割好的左区间的size去切割前序。
经过对前序和中序的切割,我们就拿到了左前序和右前序区间,左中序和右中序区间,利用左前序和左中序、右前序和右中序递归处理左、右子树···重复直到构造完成
有了大致构建思路后,先搭建整体框架,再去想怎么实现以及细节方面
第一种、未优化的版本
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (preorder.size() == 0) return nullptr;
//1、用先序找到根节点的值
int rootValue = preorder[0];
TreeNode* root = new TreeNode(rootValue);
if (preorder.size() == 1) return root;
//2、利用根的值在中序中找到根的位置
int i = 0;
while (inorder[i] != rootValue) i++;
//3、利用根的位置i切割中序的左右区间 [0,i) i [i+1,end)
vector<int> leftInorder(inorder.begin(), inorder.begin() + i);
vector<int> rightInorder(inorder.begin() + i + 1, inorder.end());
//4、利用中序的左区间切割先序的左右区间 [1,i+1) [i+1,end)
vector<int> leftPreorder(preorder.begin() + 1, preorder.begin() + i + 1);
vector<int> rightPreorder(preorder.begin() + i + 1, preorder.end());
//5、递归处理左区间和右区间
root->left = buildTree(leftPreorder, leftInorder);//利用左先序和左中序
root->right = buildTree(rightPreorder, rightInorder);//利用右先序和右中序
return root;
}
🧐优化:
优化一、
- 为什么需要该优化?
因为第二步每次都需要扫描中序遍历才能找到根节点,时间复杂度较高。而使用哈希表可以快速定位根节点,时间复杂度只需O(1)。
- 怎么样优化?
用哈希map进行哈希映射,key为节点的值,value为其在中序遍历中的出现位置,在构造二叉树之前遍历一遍中序遍历,就可以构造出哈希映射,往后构造的过程中就只需要O(1)的时间就能快速在中序中定位根节点
优化二、
- 为什么需要该优化?
因为每次递归(第三、四步)都需要生成中序和前序序列的一部分的拷贝,时间和空间复杂度都较高。
- 怎么样优化?
可以在递归函数参数中传入下标索引——通过下标索引直接在原数组上操作,用下标“切割”前序(中序)以表示区间,这样就不需要多次拷贝容器了
第二种、优化后的版本
unordered_map<int, int> hash;
// 防止晕乎先说明--要传四个参数:
// preorder容器、先序区间的起始和结束下标、中序区间的起始下标
// 其它的你会发现都用不到(~ ̄▽ ̄)~
TreeNode* traversal(vector<int>& preorder, int preStart, int preEnd, int inStart) {
//要传哪些参数呢?因为要减少容器的拷贝,所以用下标表示序列区间
//因为要根据先序第一个位置得到根节点的值,因此需要传先序序列的引用和先序区间起始位置preStart,
//且要根据中序左区间--[中序起始下标,i)的大小切割先序左右区间,因此要传中序的起始位置inStart,
//又因为需要根据先序(或中序)区间的size判断是否结束递归(区间为空就结束),因此需要传先序的结束位置preEnd。
//(这里先序区间我采用左闭右开[ )表示)
if (preStart == preEnd) return nullptr;//当先序区间大小为0时
//1、用先序找到根节点的值--位于先序序列第一个位置
int rootValue = preorder[preStart];
TreeNode* root = new TreeNode(rootValue);
//2、利用根的值在中序中找到根的位置
int i = hash[rootValue];//优化一、利用哈希映射,时间复杂度O(1)
//3、利用根的位置i切割中序--[inStart, i) i [i+1, end)
//4、利用中序的左区间大小len, 切割先序--preStart [preStart+1, preStart+1+len) [preStart+1+len, end)
int len = i - inStart;//中序的左区间大小
//5、递归处理左区间和右区间
root->left = traversal(preorder, preStart + 1, preStart + 1 + len, inStart);//左先序和左中序
root->right = traversal(preorder, preStart + 1 + len, preEnd, i + 1);//右先序和右中序
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
//构造哈希映射,以在之后建树过程中快速定位根节点
int i = 0;
for(auto elem : inorder) {
//因为是利用根的值在中序中找到根的位置,因此key是结点值而不是结点的下标
hash[elem] = i++;
}
return traversal(preorder, 0, preorder.size(), 0);
}
做完可以顺带把106、利用中序和后序构造二叉树和654、最大二叉树做了,如果弄懂了这题的话这两题应该是能分分钟秒了
做了
如果对你有帮助的话那就太好啦🤗ヾ(≧▽≦*)o
文章讲述了如何构建二叉树,首先介绍了未优化的版本,然后重点优化了查找根节点和切割序列的部分,通过哈希表和传递下标索引来减少时间和空间复杂度。

247

被折叠的 条评论
为什么被折叠?



