二叉树的构造
可以构建一棵二叉树的几个方法:
- 扩展二叉树的先序遍历序列
- 前序遍历序列和中序遍历序列
- 后序遍历序列和中序遍历序列
- 层序遍历序列和中序遍历序列
注意,只有前序遍历序列和后序遍历序列是不能唯一的确定一棵二叉树的。
扩展二叉树的先序遍历序列构造二叉树
题目描述:
在二叉树的所有空指针位置加入虚拟结点,就构成扩展二叉树。二叉树原有的结点称为内结点,新增的虚拟节点称为外结点。
设已知扩展二叉树的先序遍历序列,并用在原二叉树中不可能出现的值作为虚拟结点的值,如 “#” 表示外结点的值。
设计一个算法,使用这样一个先序遍历序列,建立树的二叉链表。
算法思想:
每读入一个值,若该值为"#",则返回空,继续读入下一个值;如果该值不为"#",那么就为它建立结点,结点值为该值,然后将该结点作为根结点,其地址通过函数的引用型参数root直接链接到作为实际参数的指针中。然后分别对左、右子树递归的建立子树。
实现代码:
//扩展二叉树的先序遍历序列
void BuildTree_Pre(BNode * & root, char pre[], int & i){
if(pre[i] == '#'){ //外结点,即空结点
i++;
root = NULL;
}else{
root = (BNode *)malloc(sizeof(BNode)); //建立根结点
root->data = pre[i];
i++;
BuildTree_Pre(root->lchild, pre, i); //递归建立左子树
BuildTree_Pre(root->rchild, pre, i); //递归建立右子树
}
}
另外,还有一种写法请参照二叉树遍历应用之根据前序遍历建树
思路是一致的,如果不理解引用型参数的可以参照这篇文章中的写法。
实例:
如上图所示,所有结点值为"#"的结点位于原二叉树的空子树结点的位置,按照先序遍历所得到的先序遍历序列为ABC##DE#G##F###。
若要建立如上所示的二叉树,则输入就应为ABC##DE#G##F###。
这里给出可执行的完整代码,为了验证构造的二叉树,构造完二叉树后,再输出其层序遍历的结果。
#include<bits/stdc++.h>
using namespace std;
typedef struct BNode{
char data;
struct BNode *lchild;
struct BNode *rchild;
}BNode;
//扩展二叉树的先序遍历序列
void BuildTree_Pre(BNode * & root, char pre[], int & i){
if(pre[i] == '#'){ //外结点,即空结点
i++;
root = NULL;
}else{
root = (BNode *)malloc(sizeof(BNode)); //建立根结点
root->data = pre[i];
i++;
BuildTree_Pre(root->lchild, pre, i); //递归建立左子树
BuildTree_Pre(root->rchild, pre, i); //递归建立右子树
}
}
//层序遍历
void levelOrder(BNode *root){
queue<BNode *> treenode;
if(root != NULL)
treenode.push(root);
while(!treenode.empty()){
BNode *p = treenode.front();
treenode.pop();
printf("%c ",p->data);
if(p->lchild != NULL)
treenode.push(p->lchild);
if(p->rchild != NULL)
treenode.push(p->rchild);
}
}
int main(){
char pre[100];
BNode *root;;
scanf("%s",pre);
int i = 0;
BuildTree_Pre(root, pre, i);
levelOrder(root);
return 0;
}
执行结果:
先序遍历序列和中序遍历序列构造二叉树
题目描述:
给定一棵二叉树的先序遍历序列pre[s1…t1]和中序遍历序列in[s2…t2],设二叉树采用二叉链表存储,设计一个算法构造这棵二叉树。
算法思想:
算法的实现步骤如下:
1)根据先序遍历序列确定树的根结点;
2)根据根结点在中序序列中的次序划分出二叉树的左子树、右子树包含哪些结点,
然后根据左、右子树结点在先序序列中的次序确定子树的根结点,即回到步骤1)。
重复上述步骤,直到每棵子树仅有一个结点(该子树的根结点)为止。
如下图所示:
若前序遍历序列为:ABCDEFGHI
中序遍历序列为:BCAEDGHFI
实现代码:
//先序遍历和中序遍历建树
BNode *PreInCreate(char pre[], char in[], int s1, int t1, int s2, int t2){
//s1,t1为先序遍历序列的第一个结点和最后一个结点的下标
//s2,t2为中序遍历序列的第一个结点和最后一个结点的下标
//初始时s1=s2=0, t1=t2=n-1
BNode *root = (BNode *)malloc(sizeof(BNode)); //建立根结点
root->data = pre[s1]; //根结点
int i;
for(i=s2; in[i]!=root->data; i++); //找根结点在中序遍历序列中的位置
int llen = i-s2; //左子树长度
int rlen = t2-i; //右子树长度
if(llen != 0) //递归建立左子树
root->lchild = PreInCreate(pre, in, s1+1, s1+llen, s2, s2+llen-1);
else //左子树为空
root->lchild = NULL;
if(rlen != 0)//递归建立右子树
root->rchild = PreInCreate(pre, in, t1-rlen+1, t1, t2-rlen+1, t2);
else //右子树为空
root->rchild = NULL;
return root;
}
或者,也可以使用引用型参数进行实现,实现的思路是一致的。
void PreInCreate2(BNode *&root, char pre[], char in[], int s1, int t1, int s2, int t2){
if(s1<=t1){
root = (BNode *)malloc(sizeof(BNode));
root->data = pre[s1];
root->lchild = NULL;
root->rchild = NULL;
int i;
for(i=0; in[i]!=root->data; i++);
int llen = i-s2; //左子树长度
int rlen = t2-i; //右子树长度
PreInCreate2(root->lchild, pre, in, s1+1, s1+llen, s2, s2+llen-1); //递归建立左子树
PreInCreate2(root->rchild, pre, in, t1-rlen+1, t1, t2-rlen+1, t2); //递归建立右子树
}
}
这里给出可执行的完整代码,为了验证构造的二叉树,构造完二叉树后,再输出其层序遍历的结果。
#include<bits/stdc++.h>
using namespace std;
typedef struct BNode{
char data;
struct BNode *lchild;
struct BNode *rchild;
}BNode;
//层序遍历
void levelOrder(BNode *root){
queue<BNode *> treenode;
if(root != NULL)
treenode.push(root);
while(!treenode.empty()){
BNode *p = treenode.front();
treenode.pop();
printf("%c ",p->data);
if(p->lchild != NULL)
treenode.push(p->lchild);
if(p->rchild != NULL)
treenode.push(p->rchild);
}
}
//先序遍历和中序遍历建树
BNode *PreInCreate(char pre[], char in[], int s1, int t1, int s2, int t2){
//s1,t1为先序遍历序列的第一个结点和最后一个结点的下标
//s2,t2为中序遍历序列的第一个结点和最后一个结点的下标
//初始时s1=s2=0, t1=t2=n-1
BNode *root = (BNode *)malloc(sizeof(BNode)); //建立根结点
root->data = pre[s1]; //根结点
int i;
for(i=s2; in[i]!=root->data; i++); //找根结点在中序遍历序列中的位置
int llen = i-s2; //左子树长度
int rlen = t2-i; //右子树长度
if(llen != 0) //递归建立左子树
root->lchild = PreInCreate(pre, in, s1+1, s1+llen, s2, s2+llen-1);
else //左子树为空
root->lchild = NULL;
if(rlen != 0)//递归建立右子树
root->rchild = PreInCreate(pre, in, t1-rlen+1, t1, t2-rlen+1, t2);
else //右子树为空
root->rchild = NULL;
return root;
}
void PreInCreate2(BNode *&root, char pre[], char in[], int s1, int t1, int s2, int t2){
//s1,t1为先序遍历序列的第一个结点和最后一个结点的下标
//s2,t2为中序遍历序列的第一个结点和最后一个结点的下标
//初始时s1=s2=0, t1=t2=n-1
if(s1<=t1){
root = (BNode *)malloc(sizeof(BNode));
root->data = pre[s1];
root->lchild = NULL;
root->rchild = NULL;
int i;
for(i=0; in[i]!=root->data; i++);
int llen = i-s2; //左子树长度
int rlen = t2-i; //右子树长度
PreInCreate2(root->lchild, pre, in, s1+1, s1+llen, s2, s2+llen-1); //递归建立左子树
PreInCreate2(root->rchild, pre, in, t1-rlen+1, t1, t2-rlen+1, t2); //递归建立右子树
}
}
int main(){
char pre[100];
char in[100];
BNode *root;
BNode *root2;
int s1=0,s2=0;
int t1,t2;
scanf("%s",pre);
scanf("%s",in);
t1 = strlen(pre)-1;
t2 = strlen(in)-1;
root = PreInCreate(pre, in, s1, t1, s2, t2);
levelOrder(root);
printf("\n");
PreInCreate2(root2, pre, in, s1, t1, s2, t2);
levelOrder(root2);
return 0;
}
执行结果:
后序遍历序列和中序遍历序列构造二叉树
题目描述:
给定一棵二叉树的后序遍历序列post[s1…t1]和中序遍历序列in[s2…t2],设二叉树采用二叉链表存储,设计一个算法构造这棵二叉树。
算法思想:
算法的实现步骤如下:
1)根据后序遍历序列确定树的根结点;
2)根据根结点在中序序列中的次序划分出二叉树的左子树、右子树包含哪些结点,
然后根据左、右子树结点在后序序列中的次序确定子树的根结点,即回到步骤1)。
重复上述步骤,直到每棵子树仅有一个结点(该子树的根结点)为止。
注意,这里与先序序列和中序序列构造二叉树的不同之处在于,先递归建立右子树,再递归建立左子树,这是由于
如下图所示:
若后序遍历序列为:CBEHGIFDA
中序遍历序列为:BCAEDGHFI
实现代码:
BNode *PostInCreate(char post[], char in[], int s1, int t1, int s2, int t2){
//s1,t1为后序遍历序列的第一个结点和最后一个结点的下标
//s2,t2为中序遍历序列的第一个结点和最后一个结点的下标
//初始时s1=s2=0, t1=t2=n-1
BNode *root = (BNode *)malloc(sizeof(BNode)); //建立根结点
root->data = post[t1]; //根结点
int i;
for(i=s2; in[i]!=root->data; i++); //找根结点在中序遍历序列中的位置
int llen = i-s2; //左子树长度
int rlen = t2-i; //右子树长度
if(llen != 0) //递归建立左子树
root->lchild = PostInCreate(post, in, s1, s1+llen-1, s2, s2+llen-1);
else //左子树为空
root->lchild = NULL;
if(rlen != 0) //递归建立右子树
root->rchild = PostInCreate(post, in, t1-rlen, t1-1, t2-rlen+1, t2);
else //右子树为空
root->rchild = NULL;
return root;
}
或者,也可以使用引用型参数进行实现,实现的思路是一致的。
void PostInCreate2(BNode *&root, char post[], char in[], int s1, int t1, int s2, int t2){
//s1,t1为后序遍历序列的第一个结点和最后一个结点的下标
//s2,t2为中序遍历序列的第一个结点和最后一个结点的下标
//初始时s1=s2=0, t1=t2=n-1
if(s1<=t1){
root = (BNode *)malloc(sizeof(BNode));
root->data = post[t1];
root->lchild = NULL;
root->rchild = NULL;
int i;
for(i=0; in[i]!=root->data; i++); //找中序遍历序列中的根结点的位置
int llen = i-s2; //左子树长度
int rlen = t2-i; //右子树长度
PostInCreate2(root->lchild, post, in, s1, s1+llen-1, s2, s2+llen-1); //递归建立左子树
PostInCreate2(root->rchild, post, in, t1-rlen, t1-1, t2-rlen+1, t2); //递归建立右子树
}
}
这里给出可执行的完整代码,为了验证构造的二叉树,构造完二叉树后,再输出其层序遍历的结果。
#include<bits/stdc++.h>
using namespace std;
typedef struct BNode{
char data;
struct BNode *lchild;
struct BNode *rchild;
}BNode;
//层序遍历
void levelOrder(BNode *root){
queue<BNode *> treenode;
if(root != NULL)
treenode.push(root);
while(!treenode.empty()){
BNode *p = treenode.front();
treenode.pop();
printf("%c ",p->data);
if(p->lchild != NULL)
treenode.push(p->lchild);
if(p->rchild != NULL)
treenode.push(p->rchild);
}
}
BNode *PostInCreate(char post[], char in[], int s1, int t1, int s2, int t2){
//s1,t1为后序遍历序列的第一个结点和最后一个结点的下标
//s2,t2为中序遍历序列的第一个结点和最后一个结点的下标
//初始时s1=s2=0, t1=t2=n-1
BNode *root = (BNode *)malloc(sizeof(BNode)); //建立根结点
root->data = post[t1]; //根结点
int i;
for(i=s2; in[i]!=root->data; i++); //找根结点在中序遍历序列中的位置
int llen = i-s2; //左子树长度
int rlen = t2-i; //右子树长度
if(llen != 0) //递归建立左子树
root->lchild = PostInCreate(post, in, s1, s1+llen-1, s2, s2+llen-1);
else //左子树为空
root->lchild = NULL;
if(rlen != 0) //递归建立右子树
root->rchild = PostInCreate(post, in, t1-rlen, t1-1, t2-rlen+1, t2);
else //右子树为空
root->rchild = NULL;
return root;
}
void PostInCreate2(BNode *&root, char post[], char in[], int s1, int t1, int s2, int t2){
//s1,t1为后序遍历序列的第一个结点和最后一个结点的下标
//s2,t2为中序遍历序列的第一个结点和最后一个结点的下标
//初始时s1=s2=0, t1=t2=n-1
if(s1<=t1){
root = (BNode *)malloc(sizeof(BNode));
root->data = post[t1];
root->lchild = NULL;
root->rchild = NULL;
int i;
for(i=0; in[i]!=root->data; i++); //找中序遍历序列中的根结点的位置
int llen = i-s2; //左子树长度
int rlen = t2-i; //右子树长度
PostInCreate2(root->lchild, post, in, s1, s1+llen-1, s2, s2+llen-1); //递归建立左子树
PostInCreate2(root->rchild, post, in, t1-rlen, t1-1, t2-rlen+1, t2); //递归建立右子树
}
}
int main(){
char post[100];
char in[100];
BNode *root;
BNode *root2;
int s1=0,s2=0;
int t1,t2;
scanf("%s",post);
scanf("%s",in);
t1 = strlen(post)-1;
t2 = strlen(in)-1;
root = PostInCreate(post, in, s1, t1, s2, t2);
levelOrder(root);
printf("\n");
PostInCreate2(root2, post, in, s1, t1, s2, t2);
levelOrder(root2);
return 0;
}
执行结果:
层序遍历序列和中序遍历序列构造二叉树
题目描述:
给定一棵二叉树的层序遍历序列level[s1…t1]和中序遍历序列in[s2…t2],设二叉树采用二叉链表存储,设计一个算法构造这棵二叉树。
算法思想:
算法的实现描述如下:
1.根据层序遍历序列第一个结点确定根结点;
2.根据根结点在中序遍历序列中分割出左右子树的中序序列;
3.根据分割出的左右子树的中序序列从层序序列中提取出对应的左右子树的层序序列;
4.对左子树和右子树分别递归使用相同的方式继续分解;
如下图所示:
若层序遍历序列为:ABDCEFGIH
中序遍历序列为:BCAEDGHFI
实现代码:
//层序遍历序列和中序遍历序列建立二叉树
BNode *LevelInCreate(char level[], char in[], int n){
char left[N]; //存储左子树的层序遍历
char right[N]; //存储右子树的层序遍历
int lcnt=0, rcnt=0;
if(n==0)return NULL;
//建立根结点
BNode *root = (BNode *)malloc(sizeof(BNode));
root->data = level[0];
int i;
for(i=0; in[i]!=root->data; i++); //在中序遍历中找到根结点的位置
//划分左右子树
for(int k=0; k<n; k++){
//查找左子树的层序遍历
for(int l=0; l<i; l++){
if(in[l] == level[k])
left[lcnt++] = level[k];
}
//查找右子树的层序遍历
for(int m=i+1; m<n; m++){
if(in[m] == level[k])
right[rcnt++] = level[k];
}
}
root->lchild = LevelInCreate(left, in, lcnt);
root->rchild = LevelInCreate(right, in+i+1, rcnt);
return root;
}
这里给出可执行的完整代码,为了验证构造的二叉树,构造完二叉树后,再输出其前序遍历的结果。
#include<bits/stdc++.h>
using namespace std;
typedef struct BNode{
char data;
struct BNode *lchild;
struct BNode *rchild;
}BNode;
const int N = 100;
//层序遍历序列和中序遍历序列建立二叉树
BNode *LevelInCreate(char level[], char in[], int n){
char left[N]; //存储左子树的层序遍历
char right[N]; //存储右子树的层序遍历
int lcnt=0, rcnt=0;
if(n==0)return NULL;
//建立根结点
BNode *root = (BNode *)malloc(sizeof(BNode));
root->data = level[0];
int i;
for(i=0; in[i]!=root->data; i++); //在中序遍历中找到根结点的位置
//划分左右子树
for(int k=0; k<n; k++){
//查找左子树的层序遍历
for(int l=0; l<i; l++){
if(in[l] == level[k])
left[lcnt++] = level[k];
}
//查找右子树的层序遍历
for(int m=i+1; m<n; m++){
if(in[m] == level[k])
right[rcnt++] = level[k];
}
}
root->lchild = LevelInCreate(left, in, lcnt);
root->rchild = LevelInCreate(right, in+i+1, rcnt);
return root;
}
//前序遍历
void PreOrder(BNode *root){
if(root!=NULL){
printf("%c ",root->data);
PreOrder(root->lchild);
PreOrder(root->rchild);
}
}
int main(){
BNode *root;
char level[N];
char in[N];
scanf("%s",level);
scanf("%s",in);
int n = strlen(level);
root = LevelInCreate(level, in, n);
PreOrder(root);
return 0;
}
执行结果: