leetcode 106. 从中序与后序遍历序列构造二叉树
Posted 大忽悠爱忽悠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode 106. 从中序与后序遍历序列构造二叉树相关的知识,希望对你有一定的参考价值。
思路分析:解决二叉树的问题通常会用到分治思想,分治思想一般通过递归方法实现。
分治法的思想:把原问题分解(Divide)成若干个与原问题结构相同但规模更小的子问题,待子问题解决(Conquer)以后,再合并(Combine)它们,原问题就得以解决。
以题目中给出的例子为例,讲解如何构建二叉树。
中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
方法一:在递归方法中,传入数组的拷贝(不推荐、复杂度较高)
- 该方法在计算索引的时候会稍微容易一些。
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
{
int inSize = inorder.size();
int postSize = postorder.size();
//当前为空节点
if (inSize == 0)
return NULL;
//当前只有一个节点剩余---当前节点为叶子节点
if (inSize == 1)
return new TreeNode(inorder[0]);
//给当前根节点赋值--当前根节点就是当前后序遍历数组最后一个元素
TreeNode* root = new TreeNode(postorder[postSize - 1]);
//找到当前中序遍历数组中根节点的下标位置
int divPoint = 0;
for (int i = 0; i < inSize; i++)
{
if (inorder[i] == postorder[postSize - 1])
{
divPoint = i;
break;
}
}
//构造一个数组包含当前中序遍历左半部分(左子树构成的左半部分)内容,对后序部分用以同样的操作
vector<int> in1(inorder.begin(), inorder.begin() + divPoint);
vector<int> pos1(postorder.begin(), postorder.begin() + divPoint);//这里都是加divpoint的原因是左子树整体长度一致
//再构造两个数组分别包含中序和后序的右半部分
vector<int> in2(inorder.begin() + divPoint+1,inorder.begin()+inSize);
vector<int> pos2(postorder.begin() + divPoint,postorder.begin()+postSize-1);//注意这里后序遍历右半部分数组的构成,因为是左闭右开的区间
root->left = buildTree(in1, pos1);//找到以当前左孩子为首的根节点,并返回----去当前根节点的左子树里面寻找
root->right = buildTree(in2, pos2);//找到以当前右孩子为首的根节点,并返回-----去当前根节点的右子树里面寻找
return root;
}
};
方法二:在递归方法中,传入子数组的边界下标
注意:在递归方法中,有一个数组的边界下标。
- 计算的依据是递归方法传入的中序遍历数组(的子数组)和后序遍历数组(的子数组)的长度相等。我的办法是解方程计算未知数,具体需要计算哪个参数我在下面的代码中已经注明了。
- 下面展示了一个计算边界的方法。
代码:
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
{
int inSize = inorder.size();
int postSize = postorder.size();
return buildTree(inorder, postorder, 0, inSize - 1, 0, postSize - 1);
}
//inLeft:中序遍历当前数组起点 inRight:中序遍历当前数组终点 postLeft:后序遍历当前数组起点 postRight:后序遍历当前数组终点
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder, int inLeft, int inRight, int postLeft, int postRight)
{
//结束条件:指针错位----当前部分为空节点
if (inLeft > inRight || postLeft > postRight)
return NULL;
//通过当前后序部分的终点下标来获取当前根节点的值
int pivot=postorder[postRight];
//把当前中序部分的起点的下标用临时变量保存
int pivotIndex = inLeft;
//通过起点不断后移,比对,找到根节点在中序数组的下标
// 注意这里如果编写不当,有数组下标越界的风险
while (inorder[pivotIndex] != pivot) { pivotIndex++; }
//构造当前根节点
TreeNode* root = new TreeNode(pivot);
//给当前根节点左孩子赋值
root->left = buildTree(inorder, postorder, inLeft,pivotIndex - 1,
postLeft, postRight - inRight + pivotIndex - 1);
//给当前根节点的右孩子赋值
root->right = buildTree(inorder, postorder, pivotIndex + 1, inRight,
postRight - inRight + pivotIndex,postRight-1);
//返回当前根节点
return root;
}
};
方法三:使用哈希表进行查找优化
- 可以在递归构造前,把中序遍历的值和下标放在哈希表中,就不需要通过遍历得到当前根结点在中序遍历中的位置了。
class Solution {
vector<int> postorder; //让 postorder 成为全局变量,以免在递归方法中一直传递
unordered_map<int, int> map;
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
{
int inSize = inorder.size();
int postSize = postorder.size();
this->postorder = postorder;
// 将节点值在inorder数组中的位置提前存入map
for (int i = 0; i < inSize; i++)
map[inorder[i]] = i;
return buildTree(0, inSize - 1, 0, postSize - 1);
}
//inLeft:中序遍历当前数组起点 inRight:中序遍历当前数组终点 postLeft:后序遍历当前数组起点 postRight:后序遍历当前数组终点
TreeNode* buildTree(int inLeft, int inRight, int postLeft, int postRight)
{
//结束条件:指针错位----当前部分为空节点
if (inLeft > inRight || postLeft > postRight)
return NULL;
//构造当前根节点
TreeNode* root = new TreeNode(postorder[postRight]);
//获取根节点在当前中序数组中的下标位置
int pivotIndex = map[postorder[postRight]];
//给当前根节点左孩子赋值
root->left = buildTree(inLeft,pivotIndex - 1,
postLeft, postRight - inRight + pivotIndex - 1);
//给当前根节点的右孩子赋值
root->right = buildTree(pivotIndex + 1, inRight,
postRight - inRight + pivotIndex,postRight-1);
//返回当前根节点
return root;
}
};
以上是关于leetcode 106. 从中序与后序遍历序列构造二叉树的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode Java刷题笔记—106. 从中序与后序遍历序列构造二叉树
LeetCode Java刷题笔记—106. 从中序与后序遍历序列构造二叉树