题目

题目链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/


初步题解

先放代码:

/**
* 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:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { // TreeNode* re = nullptr;
if(preorder.empty()){
return nullptr;
}
//根节点
TreeNode* root = new TreeNode(preorder[0]);
// root->val = inorder[0]; vector<int> lPreorder,rPreorder;
vector<int> lInorder,rInorder;
int midIndex = 0; for(int i = 0; i<inorder.size() ; i++){
if(inorder[i]==preorder[0]){
midIndex = i;
break;
}
}
// 获取左右子树的结点数量
int leftSize = midIndex;
int rightSize = preorder.size() - midIndex - 1; // 分开赋值
for(int i=0 ; i<midIndex ; i++){
lInorder.push_back(inorder[i]);
// lPreorder[i]=preorder[i];
}
for(int i=midIndex+1 ; i<inorder.size() ; i++){
rInorder.push_back(inorder[i]);
// lPreorder[i-midIndex-1]=preorder[i];
} for(int i=1 ; i<leftSize+1 ; i++){
lPreorder.push_back(preorder[i]);
// lInorder[i-1] = inorder[i];
}
for(int i=leftSize+1 ; i<preorder.size() ; i++){
rPreorder.push_back(preorder[i]);
// rInorder[i-1] = inorder[i];
} //左子树
root->left = buildTree(lPreorder,lInorder);
//右子树
root->right = buildTree(rPreorder,rInorder); return root;
}
};

思路很简单,先序遍历时,根节点的值会被放在第一个。而中序遍历时,根节点的值刚好把左右子树全部的值分隔开,就是利用这一句话的思路做题。我们只要先按照先序遍历分隔开中序遍历为左子树和右子树的值,然后在分别对左右子树进行递归操作就可以了。

递归退出点的选择:返回的值是树的结点指针,也就是作为父节点的左右子结点来用。这样只要最后不满足条件时返回null就可以了。至于退出条件,就是传入的先序遍历(或后序遍历)所承载的数组为空就可以退出了。

但是这样做结果很慢:

而实际上比较快的只需要24ms,足足慢了10倍,必须要优化。

优化

存在两个优化点。第一个是每次都要搜索root节点在中序遍历中的位置,第二个是每次都要来回创建新的vector来存放中序遍历的左右子树再往下递归,这个创建vector并赋值的过程其实很耗时间。

  • 针对一个点,可以创建一个全局的map来避免每次都查找,不然每一次递归都要寻找根节点的位置。

  • 针对第二个点,选择传index,而不是重新赋值来做,这样节省了大量的时间。

优化后的代码

/**
* 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:
map<int,int> inorder_map;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
for(int i=0;i<inorder.size();i++){
// 方便一下找到大小为i的元素在哪个位置了,省去了遍历浪费的时间
inorder_map[inorder[i]]=i;
}
TreeNode* root = new TreeNode;
root = Recur_buildTree(preorder, inorder, 0, preorder.size()-1, 0, inorder.size()-1);
return root;
} TreeNode* Recur_buildTree(vector<int>& preorder, vector<int>& inorder, int pre_start, int pre_end, int in_start, int in_end){
if(pre_start > pre_end || in_start > in_end){
return nullptr;
}
//根节点
TreeNode* root = new TreeNode(preorder[pre_start]); int mid_index = inorder_map[preorder[pre_start]];
// 获取左子树的结点数量
int left_size = mid_index - in_start; //左子树
root->left = Recur_buildTree(preorder, inorder, pre_start + 1, pre_start + left_size, in_start, in_start + left_size -1);
//右子树
root->right = Recur_buildTree(preorder, inorder, pre_start + left_size + 1, pre_end, mid_index+1, in_end); return root;
}
};

优化后的速度:

剑指offer 07 & LeetCode 105 重建二叉树的更多相关文章

  1. 剑指Offer - 九度1385 - 重建二叉树

    剑指Offer - 九度1385 - 重建二叉树2013-11-23 23:53 题目描述: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的 ...

  2. 剑指offer面试题6 重建二叉树(c)

  3. 剑指offer面试题6 重建二叉树(java)

    注:(1)java中树的构建 (2)构建子树时可以直接利用Arrays.copyOfRange(preorder, from, to),这个方法是左开右闭的 package com.xsf.SordF ...

  4. 剑指offer第二版-7.重建二叉树

    描述:输入某二叉树的前序遍历和中序遍历结果,重建该二叉树.假设前序遍历或中序遍历的结果中无重复的数字. 思路:前序遍历的第一个元素为根节点的值,据此将中序遍历数组拆分为左子树+root+右子树,前序遍 ...

  5. 《剑指Offer》面试题-重建二叉树

    题目描述: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7 ...

  6. 剑指offer【04】- 重建二叉树(java)

    题目:重建二叉树 考点:树 题目描述:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6, ...

  7. 剑指offer(4)重建二叉树

    题目描述 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7, ...

  8. 【剑指offer】 Java实现重建二叉树

    GitHub上的代码链接 /** * @Author: DaleyZou * @Description: 重建二叉树 * 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树. * 假设输入的前序 ...

  9. 【剑指Offer】4、重建二叉树

      题目描述:   输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列 ...

随机推荐

  1. 【av68676164(p54)】段式和段页式虚拟存储

    段式存储管理 进程分段 把进程按逻辑意义划分为多个段,每段有段名,长度不定,进程由多段组成 例:一个具有代码段.数据段和堆栈段的进程 段式内存管理系统的内存分配 以段为的单位装入,每段分配连续的内存 ...

  2. 使用MIPS完成汇编程序——选择排序实现

    题目: 从键盘输入10个无符号字数并从大到小进行排序,排序结果在屏幕上显示出来.要求能输入数字 和输出数字 且由大到小来排列 1.代码以及伪代码: 首先写出对应c++代码然后把c++代码翻译成汇编语言 ...

  3. C# WebAPI项目,不支持HttpPut请求!!!

    有点标题党了,这个异常的现象是这样的: 我有一个正在跑的项目,要对接第三方厂家的设备.  对方给的接口文档,里面有一个接口是需要我这边实现的,要求必须是PUT请求方式. 所以我在项目基础上,新增一个W ...

  4. docker-machine在阿里云部署批量部署docker

    概述 docker入门中,docker swarm都是在本机的虚拟机上,为了更贴近生产环境,我将这部分重新部署到阿里云.不需要太贵,选最便宜的按量付费ECS,1小时才0.05元. docker-mac ...

  5. PythonCrashCourse 第十章习题

    在文本编辑器中新建一个文件,写几句话来总结一下你至此学到的Python知识,其中每一行都以"In Python you can"打头.将这个文件命名为 learning_pytho ...

  6. graphics.h源代码下载

    graphics.h源代码下载 /*graphics.h DefinitionsforGraphicsPackage. Copyright(c)BorlandInternational1987,198 ...

  7. 思索-js 页面ID识别及数据缓存

    思索-页面ID识别及数据缓存 页面 ID 识别的思路 多页应用在移动端是较为常见的一种架构,它可以和APP 内的 webview 配合,达到类似原生的体验,这一点是单页应用无法做到的(比如手势滑动等, ...

  8. VS Code安装yo(Yeoman) 插件下载.net core 模版代码开发

    在安装插件以前,请看插件地址的相关依赖 Pre-requirements [Node.js] (https://nodejs.org) [npm] (https://www.npmjs.com) [Y ...

  9. [CSP-S2019]括号树 题解

    CSP-S2 2019 D1T2 刚开考的时候先大概浏览了一遍题目,闻到一股浓浓的stack气息 调了差不多1h才调完,加上T1用了1.5h+ 然而T3还是没写出来,滚粗 思路分析 很容易想到的常规操 ...

  10. ssm框架之springMVC拦截器

    1拦截器概述 1.1什么是拦截器? springMVC中的拦截器(Interceptor)类似于servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理.例如通过拦截器可以进行权 ...