从中序与后序遍历序列构造二叉树

力扣题目链接(opens new window)

根据一棵树的中序遍历与后序遍历构造二叉树。

注意: 你可以假设树中没有重复的元素。

例如,给出

中序遍历 inorder = [9,3,15,20,7] 后序遍历 postorder = [9,15,7,20,3] 返回如下的二叉树:

思路

题目给了目标二叉树的中序遍历(左中右)和后序遍历(左右中)结果数组

那么我们可以通过后序遍历的结果数组的最后一个值确定该二叉树的根节点

有了根节点,下一步就要确定根节点两侧的树的节点,这时就需要使用根节点分别在中序和后序结果数组中分割(一定是先中序再后序)

按顺序在中序遍历和后序遍历的结果数组中使用该值进行数组分割(注意,这里的左右是针对当前所分割的数组来说的)

  • 先分割中序遍历的结果数组,得到中序左数组和中序右数组
  • 再分割后序遍历的结果数组,得到后序左数组和后序右数组

完成分割之后,再使用递归对左右区间进行相同的处理,直到最后碰到叶子节点

图示如下:

上图例子中,先通过后序遍历数组([9,15,7,20,3])的最后一个数确认根节点(中间节点) 3

先在中序遍历数组([9,3,15,20,7])中按 3 进行分割,得到左边部分([9])和右边部分([15,20,7]),即中序左数组和中序右数组

然后在后序遍历数组([9,15,7,20,3])中进行分割

因为 3 在后序遍历数组的最后一位,所以不能用来做分割点位,并且也不能继续保留了

那怎么分割后序数组呢?

这里有一个规律:中序遍历数组和后序遍历数组在整体大小上永远是相同的

我们既然已经分割好了中序遍历数组,那么就可以根据左边中序遍历数组(不能是右边)的大小来对后序遍历数组进行分割

后序遍历数组([9,15,7,20,3])分割好后得到左边部分([9])和右边部分([15,7,20]),即后序左数组和后序右数组

此时我们需要进行递归对 左中序/后序 和 右中序/后序 进行处理,处理逻辑同上(也是使用后序数组确定中节点,分割中序数组,再通过中序数组分割后序数组)

代码

代码实现的步骤一共分以下几步:

  • 第一步:如果数组大小为零的话,说明是空节点了。
  • 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
  • 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
  • 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
  • 第五步:切割后序数组,切成后序左数组和后序右数组
  • 第六步:递归处理左区间和右区间

本题的代码比较多,可以先把整体框架写出来再补充

框架
//递归函数的返回值应
TreeNode* traversal(vector<int>& inorder, vector<int>& postorder){
//确认数组是否为0
if(postorder.size() == 0) return NULL; //后序遍历数组的最后一个数,即根节点
int rootValue = postorder[postorder.size() - 1];
//创建根节点
TreeNode* root = new TreeNode(rootValue); //如果当前postorder只有一个值,那根节点就是叶子节点,可直接返回
if(postorder.size() == 1) return root; //遍历中序遍历数组,寻找切割点
//注意,是在中序遍历数组找出与rootValue值相同的元素的 下标
int cutIndex;
for(cutIndex = 0; cutIndex < inorder.size(); ++cutIndex){
//遇到目标值就结束遍历
if(inorder[cutIndex] == rootValue) break;
} // 第四步:切割中序数组,得到 中序左数组和中序右数组
// 第五步:切割后序数组,得到 后序左数组和后序右数组 //第六步,调用递归处理root两侧的节点
root->left = traversal(中序左数组, 后序左数组);
root->right = traversal(中序数右组, 后序右数组);
}
切割数组

定义vector保存切割中序/后序数组的结果

//递归函数的返回值应
TreeNode* traversal(vector<int>& inorder, vector<int>& postorder){
... //遍历中序遍历数组,寻找切割点
//注意,是在中序遍历数组找出与rootValue值相同的元素的 下标
int cutIndex;
for(cutIndex = 0; cutIndex < inorder.size(); ++cutIndex){
//遇到目标值就结束遍历
if(inorder[cutIndex] == rootValue) break;
} // 第四步:切割中序数组,得到 中序左数组和中序右数组
// 左闭右开区间:[0, delimiterIndex)
vector<int> leftInorder(inorder.begin(), inorder.begin() + cutIndex);
vector<int> rightInorder(inorder.begin() + cutIndex + 1, inorder.end());
// 第五步:切割后序数组,得到 后序左数组和后序右数组
// 先把后序数组里面的最后一个元素舍弃,,那个节点已经作为根节点了
postorder.resize(postorder.size() - 1);
vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
vector<int> rightPostorder(postorder.begin() + leftInorder.size() + 1, postorder.end());
...
}

之后再调用递归,按照相同逻辑重复处理分割出来的区间

完整代码
class Solution {
public:
//递归函数的返回值应
TreeNode* traversal(vector<int>& inorder, vector<int>& postorder){
//确认数组是否为0
if(postorder.size() == 0) return NULL; //后序遍历数组的最后一个数,即根节点
int rootValue = postorder[postorder.size() - 1];
//创建根节点
TreeNode* root = new TreeNode(rootValue); //如果当前postorder只有一个值,那根节点就是叶子节点,可直接返回
if(postorder.size() == 1) return root; //遍历中序遍历数组,寻找切割点
//注意,是在中序遍历数组找出与rootValue值相同的元素的 下标
int cutIndex;
for(cutIndex = 0; cutIndex < inorder.size(); ++cutIndex){
//遇到目标值就结束遍历
if(inorder[cutIndex] == rootValue) break;
} // 第四步:切割中序数组,得到 中序左数组和中序右数组
// 左闭右开区间:[0, delimiterIndex)
vector<int> leftInorder(inorder.begin(), inorder.begin() + cutIndex);
// [delimiterIndex + 1, end)
// 这里加1是为了跳过根节点3
vector<int> rightInorder(inorder.begin() + cutIndex + 1, inorder.end());
// 第五步:切割后序数组,得到 后序左数组和后序右数组
// 先把后序数组里面的最后一个元素舍弃,那个节点已经作为根节点了
// 依然左闭右开,注意这里使用了左中序数组大小作为切割点
// [0, leftInorder.size)
postorder.resize(postorder.size() - 1);
vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
// [leftInorder.size(), end)
//这里不用加1是因为3已经在前面被删除了
vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end()); /*// 以下为日志
cout << "----------" << endl; cout << "leftInorder :";
for (int i : leftInorder) {
cout << i << " ";
}
cout << endl; cout << "rightInorder :";
for (int i : rightInorder) {
cout << i << " ";
}
cout << endl; cout << "leftPostorder :";
for (int i : leftPostorder) {
cout << i << " ";
}
cout << endl;
cout << "rightPostorder :";
for (int i : rightPostorder) {
cout << i << " ";
}
cout << endl;*/ //第六步,调用递归处理root两侧的节点
root->left = traversal(leftInorder, leftPostorder);
root->right = traversal(rightInorder, rightPostorder); return root;
} TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
//判断所给数组是否为空
if (inorder.size() == 0 || postorder.size() == 0) return NULL;
return traversal(inorder, postorder);
}
};
注意点

0、先把框架写了再填内容

1、分割中序数组时,记得不要把3再加进来

2、别忘了vector的构造方式

void test01()
{
vector<int> v1; //无参构造
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1); vector<int> v2(v1.begin(), v1.end());
printVector(v2); vector<int> v3(10, 100);
printVector(v3); vector<int> v4(v3);
printVector(v4);
}

从前序与中序遍历序列构造二叉树

力扣题目链接(opens new window)

根据一棵树的前序遍历与中序遍历构造二叉树。

注意: 你可以假设树中没有重复的元素。

例如,给出

前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9,3,15,20,7] 返回如下的二叉树:

思路

与使用 中序+后序 构造的思路一致

只是这里最开始时,我们需要从前序遍历数组中找根节点(即第一个元素)

代码

class Solution {
public:
TreeNode* traversal(vector<int>& preorder, vector<int>& inorder){
//从前序数组拿根节点,先判断其是否为空
if(preorder.size() == 0) return NULL; //获取根节点数值
int rootValue = preorder[0];
//创建根节点
TreeNode* root = new TreeNode(rootValue); //如果只有一个根节点,返回
if(preorder.size() == 1) return root; //在中序数组中遍历出与根节点相同值的元素的下标
int cutIndex;
for(cutIndex = 0; cutIndex < inorder.size(); ++cutIndex){
if(inorder[cutIndex] == rootValue) break;
} //分割中序数组
vector<int> leftInorder(inorder.begin(), inorder.begin() + cutIndex);
vector<int> rightInorder(inorder.begin() + cutIndex + 1, inorder.end()); //分割前序数组
vector<int> leftPreorder(preorder.begin() + 1, preorder.begin() + 1 + leftInorder.size());
vector<int> rightPreorder(preorder.begin() + 1 + leftInorder.size(), preorder.end()); // // 以下为日志
// cout << "----------" << endl; // cout << "leftInorder :";
// for (int i : leftInorder) {
// cout << i << " ";
// }
// cout << endl; // cout << "rightInorder :";
// for (int i : rightInorder) {
// cout << i << " ";
// }
// cout << endl; // cout << "leftPreorder :";
// for (int i : leftPreorder) {
// cout << i << " ";
// }
// cout << endl;
// cout << "rightPreorder :";
// for (int i : rightPreorder) {
// cout << i << " ";
// }
// cout << endl; //递归处理
root->left = traversal(leftPreorder, leftInorder);
root->right = traversal(rightPreorder, rightInorder); return root;
} TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return traversal(preorder, inorder);
}
};

【LeetCode二叉树#10】从中序与后序(或者前序)遍历序列构造二叉树(首次构造二叉树)的更多相关文章

  1. Leetcode:105. 从前序与中序遍历序列构造二叉树&106. 从中序与后序遍历序列构造二叉树

    Leetcode:105. 从前序与中序遍历序列构造二叉树&106. 从中序与后序遍历序列构造二叉树 Leetcode:105. 从前序与中序遍历序列构造二叉树&106. 从中序与后序 ...

  2. 代码随想录算法训练营day18 | leetcode 513.找树左下角的值 ● 112. 路径总和 113.路径总和ii ● 106.从中序与后序遍历序列构造二叉树

    LeetCode 513.找树左下角的值 分析1.0 二叉树的 最底层 最左边 节点的值,层序遍历获取最后一层首个节点值,记录每一层的首个节点,当没有下一层时,返回这个节点 class Solutio ...

  3. LeetCode(106):从中序与后序遍历序列构造二叉树

    Medium! 题目描述: 根据一棵树的中序遍历与后序遍历构造二叉树. 注意:你可以假设树中没有重复的元素. 例如,给出 中序遍历 inorder = [9,3,15,20,7] 后序遍历 posto ...

  4. [leetcode]从中序与后序/前序遍历序列构造二叉树

    从中序与后序遍历序列构造二叉树 根据一棵树的中序遍历与后序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 例如,给出 中序遍历 inorder = [9,3,15,20,7] 后序遍历 po ...

  5. Java实现 LeetCode 106 从中序与后序遍历序列构造二叉树

    106. 从中序与后序遍历序列构造二叉树 根据一棵树的中序遍历与后序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 例如,给出 中序遍历 inorder = [9,3,15,20,7] 后序 ...

  6. [LeetCode] Construct Binary Tree from Inorder and Postorder Traversal 由中序和后序遍历建立二叉树

    Given inorder and postorder traversal of a tree, construct the binary tree. Note: You may assume tha ...

  7. LeetCode106. 从中序与后序遍历序列构造二叉树

    106. 从中序与后序遍历序列构造二叉树 描述 根据一棵树的中序遍历与后序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 示例 例如,给出 中序遍历 inorder = [9,3,15,20 ...

  8. LeetCode二叉树的前序、中序、后序遍历(递归实现)

    本文用递归算法实现二叉树的前序.中序和后序遍历,提供Java版的基本模板,在模板上稍作修改,即可解决LeetCode144. Binary Tree Preorder Traversal(二叉树前序遍 ...

  9. [Swift]LeetCode106. 从中序与后序遍历序列构造二叉树 | Construct Binary Tree from Inorder and Postorder Traversal

    Given inorder and postorder traversal of a tree, construct the binary tree. Note:You may assume that ...

  10. 【2】【leetcode-105,106】 从前序与中序遍历序列构造二叉树,从中序与后序遍历序列构造二叉树

    105. 从前序与中序遍历序列构造二叉树 (没思路,典型记住思路好做) 根据一棵树的前序遍历与中序遍历构造二叉树. 注意:你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [ ...

随机推荐

  1. [转帖]关于Nacos默认token.secret.key及server.identity风险说明及解决方案公告

    https://nacos.io/zh-cn/blog/announcement-token-secret-key.html 近期Nacos社区收到关于Nacos鉴权功能通过token.secret. ...

  2. Linux 下面删除指定日期之前文件的办法

    1. Linux 下面最近有一个需求 需要只更新2020年4月10号之后补丁的需求 2. rsync 能够拉取所有的补丁文件  没找到能够按照日期进行拉取的办法. 所以想了一个折中的办法 先拉取 再按 ...

  3. CentOS7最小安装注意事项

    CentOS7最小安装注意事项 背景与说明 公司很多项目现在都是最小化安装的了. 自己之前为了方便都是使用GUI安装,很多地方可能都有遗漏 这里进行一下简单的安装总结, 避免以后新安装有缺失的项目 安 ...

  4. Skia 编译及踩坑实践

    本文要点 •了解并入门 Skia.OpenGL 和 Vulkan •了解 Skia 在后端渲染上的坑点 前言 Skia 是什么 Skia 是一个开源 2D 图形库,提供可跨各种硬件和软件平台工作的通用 ...

  5. js 删除数组中的某一个内容

    <script type="text/javascript"> let list=["为","啊发大水","阿斯蒂芬& ...

  6. 【踩了一个坑】为什么 golang struct 中的 slice 无法原子赋值

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 有这样一个结构体: type MySt struct{ F ...

  7. fbx查看软件

    对于3D的模型资源,比如fbx文件,除了使用专业的软件查看外,比如Unity3D,vs2015及更高版本,maya,3DMax等等,有没有更加轻量的软件可以查看fbx的内容呢? win10自带 win ...

  8. 【4】配置和运行Opencv常见的一些问题总结,以及bug解决。

    相关文章: [1]windows下安装OpenCV(4.3)+VS2017安装+opencv_contrib4.3.0配置 [2]Visual Studio 2017同时配置OpenCV2.4 以及O ...

  9. ElasticSearch-聚合、自动补全、集群、数据同步

    数据聚合 1.数据聚合 聚合(aggregations)可以让我们极其方便的实现对数据的统计.分析.运算.例如: 什么品牌的手机最受欢迎? 这些手机的平均价格.最高价格.最低价格? 这些手机每月的销售 ...

  10. 多进程实现socket通信(Python)

    服务器端: #__author__:Kelvin #date:2020/5/9 11:35 import socket from multiprocessing import Process def ...