目录
一、必备知识
1、递归构造二叉树
1、树的构造和递归我们已经很清楚了,那么核心步骤有哪些呢?
一棵二叉树由什么组成?根节点+左子树+右子树
所以,在递归二叉树时我们同样遵循此步骤
①首先,确立根节点
②紧接着,继续通过递归的方式构造该根结点的左右子树
(注意,此处一定不要忘记将这两个子树连接到前面的根节点上去)
2、那么,这时候可能会想,递归的出口是什么呢?
首先要明确一点的是,构造二叉树的递归函数的返回值是一棵树结构
所以,当我们没有可以确立的根节点时,自然返回不了一棵树了,自然该递归函数也便就结束了
故递归函数的出口即当不能继续构造时,return nullptr;
(怎么判断能不能继续构造,在不同的题目中有不同的做法)
3、具体代码实现如下
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
TreeNode* build_tree(vector<int>& nodes, int& index) {
if (index >= nodes.size() || nodes[index] == -1) {
index++;
return NULL;
}
TreeNode* root = new TreeNode(nodes[index]);
index++;
root->left = build_tree(nodes, index);
root->right = build_tree(nodes, index);
return root;
}
2、如何根据遍历序列构造一棵二叉树
1、在数据结构的学习中我们可以知道:
①根据前序序列和中序序列可以唯一确定一棵二叉树
>>Step1:通过前序序列可确定树的根结点的值(前序序列的第一个位置)
>>Step2:通过根的值在中序遍历序列中找到对应位置,便可唯一确定左子树和右子树
(此处找对应根的索引值有两种方法,第一种是每次都用一个for循环遍历中序序列,当找到对应的根值时返回根的索引值;第二种的效率更高,运用空间换时间的思想,通过建立一个哈希表,将索引和值对应起来,这样我们每次只用查找在哈希表中对应值的位置即可)
>>Step3:中序遍历中的左右子树确定了,前序遍历的左右子树自然便确定了
>>Step4:重复上面3个步骤,进一步寻找子树的根节点和左右子树(递归)
②根据后序序列和中序序列可以唯一确定一棵二叉树
(图来源:106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode))
>>Step1:通过后序序列可确定树的根结点的值(后序序列的最后一个位置)
>>Step2:通过根的值在中序遍历序列中找到对应位置,便可唯一确定左子树和右子树
>>Step3:中序遍历中的左右子树确定了,后序遍历的左右子树自然便确定了
>>Step4:重复上面3个步骤,进一步寻找子树的根节点和左右子树(递归)
通过上面的思想,我们可以知道递归构造二叉树的函数参数
1、前/后序序列(可以加const防止被更改)+中序序列
2、当前树在前/后序序列中的左边界+右边界
3、当前树在中序序列中的左边界+右边界
(因为两个序列一起才能唯一确定一棵树,所以在这两个序列中的位置都要包含)
具体代码实现如下:
二、根据前序序列和中序序列构造代码实现
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
//树的递归构造过程
//构造根+其子树
class Solution {
public:
//核心
//1、确定根节点
//2、划分子集
//哈希表建立对应关系
//在中序序列中建立哈希表
//便于找到根节点在中序序列中的位置
//但只适合“无重复结点值”的二叉树
unordered_map<int,int>index;
//递归参数的确定
//分别结合先序序列和中序序列便可以确定一棵子树
//故递归参数为子先序序列,子中序序列
TreeNode* myBuildTree(const vector<int>& preorder,const vector<int>&inorder,int preorder_left,int preorder_right,int inorder_left,int inorder_right)
{
//当左结点值大于右结点值时,递归结束
if(preorder_left>preorder_right)
{
return nullptr;
}
//第一步:找根
//根据先序序列获取根节点
int preorder_root=preorder_left;
//在中序遍历中定位根节点
int inorder_root=index[preorder[preorder_root]];
//建立根节点
TreeNode* root=new TreeNode(preorder[preorder_root]);
//第二步:建立子树
//得到左子树的节点数目
int size_left_subtree=inorder_root-inorder_left;
//递归构造时分别传入新的先序和中序序列
//递归构造左子树
root->left=myBuildTree(preorder,inorder,preorder_left+1,preorder_left+size_left_subtree,inorder_left,inorder_root-1);
//递归构造右子树
root->right=myBuildTree(preorder,inorder,preorder_left+size_left_subtree+1,preorder_right,inorder_root+1,inorder_right);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n=preorder.size();
for(int i=0;i<n;i++)
{
index[inorder[i]]=i;//存储各值在中序遍历中的索引
}
return myBuildTree(preorder,inorder,0,n-1,0,n-1);
}
};
三、根据后序序列和中序序列构造代码实现
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
//前后找根,中序找左右
//树的递归构造过程:找根+其子树
class Solution {
public:
//哈希表:存中序序列中根的位置
unordered_map<int,int>index;
//建立构造二叉树的递归函数
//中序序列:左根右
//后序序列:左右根
TreeNode* MybuildTree(const vector<int>&inorder,const vector<int>&postorder,int inorder_left,int inorder_right,int postorder_left,int postorder_right)
{
//递归出口
if(postorder_right<postorder_left)
{
return nullptr;//无法继续构造子树
}
//根据后序序列确定根结点的位置
int postorder_index=postorder_right;
//对应到中序序列中
int inorder_root=index[postorder[postorder_index]];
//开始构造树
//构造根结点
TreeNode* root=new TreeNode(inorder[inorder_root]);
//构造子树
//获取左子树的大小
int size_left_subtree=inorder_root-inorder_left;
//递归构造左子树,一定要连接到根上
root->left=MybuildTree(inorder,postorder,inorder_left,inorder_root-1,postorder_left,postorder_left+size_left_subtree-1);
//递归构造右子树
root->right=MybuildTree(inorder,postorder,inorder_root+1,inorder_right,postorder_left+size_left_subtree,postorder_right-1);
//构造完毕
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
int len=inorder.size();
//建立中序序列中根的关系
for(int i=0;i<len;i++)
{
index[inorder[i]]=i;
}
//返回递归构造的结果
return MybuildTree(inorder,postorder,0,len-1,0,len-1);
}
};
看完这两个可以举一反三一下另一种不能唯一确定的结构
889. 根据前序和后序遍历构造二叉树 - 力扣(LeetCode)
四、总结
本文详细介绍了如何通过遍历序列构造一棵二叉树的思想和代码,核心还是离不开递归和树的划分,递归是贯穿整个树的思想,一定要多加练习。