7-1 树的同构 (25 分)
给定两棵树T1和T2。如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是“同构”的。例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A、B、G的左右孩子互换后,就得到另外一棵树。而图2就不是同构的。
现给定两棵树,请你判断它们是否是同构的。
输入格式:
输入给出2棵二叉树树的信息。对于每棵树,首先在一行中给出一个非负整数N (≤10),即该树的结点数(此时假设结点从0到N−1编号);随后N行,第i行对应编号第i个结点,给出该结点中存储的1个英文大写字母、其左孩子结点的编号、右孩子结点的编号。如果孩子结点为空,则在相应位置上给出“-”。给出的数据间用一个空格分隔。注意:题目保证每个结点中存储的字母是不同的。
输出格式:
如果两棵树是同构的,输出“Yes”,否则输出“No”。
输入样例1(对应图1):
8
A 1 2
B 3 4
C 5 -
D - -
E 6 -
G 7 -
F - -
H - -
8
G - 4
B 7 6
F - -
A 5 1
H - -
C 0 -
D - -
E 2 -
输出样例1:
Yes
输入样例2(对应图2):
8
B 5 7
F - -
A 0 3
C 6 -
H - -
D - -
G 4 -
E 1 -
8
D 6 -
B 5 -
E - -
H - -
C 0 2
G - 3
F - -
A 1 4
输出样例2:
No
题意理解:
- 先在一行输入一个N,下面有N行输入
- 每一行对应一个结点,给出该结点储存的数据,左孩子编号,右孩子编号,如果孩子结点为空,那么就输入一个“-”
程序大致框架:
1.构造二叉树的结构体
2.创建二叉树
3.进行同构判断
二叉树的结构体表示:
typedef struct{
char data; //储存的数据
int lchild;//左孩子结点的位置
int rchild;//有孩子结点的位置
}TNode;
二叉树的创建:
这里给出的二叉树表现形式,适合使用顺序链表存储,即创建一个数组,数组的每个元素是一个结点类型的结构体,如果想要达到遍历二叉树的效果,只需要找出根结点在数组中的位置
int main()
{
TNode Tree1[10];//创建二叉树Tree1,因为N最大为10,所以结点最多为10个
TNode Tree2[10];//创建二叉树TREE2
//创建完树以后,将N个结点顺序输入到树种,并找出根结点所在位置
//CreatTree(TNode T[])函数,实现读取数据,并返回根节点位置的功能
int root1 = CreatTree(Tree1);//int类型的root1变量,代表Tree1中根结点的位置
int root2 = CreatTree(Tree2);//int类型的root2变量,代表Tree2中根结点的位置
进行同构判断;
}
下面是CreatTree(TNode T[])函数的实现
在这部分代码中,使用了一个JudgeRoot[]数组进行判断树种的每个结点是不是根结点,原理很简单,就是将这个数组的长度设置为和树中结点数相同,在树数组中i位置的结点如果有孩子,那么就将JudgeRoot[]数组中该结点的孩子位置设为1,遍历JudgeRoot[]数组,零所在的下标即为树中根节点的下标
int CreatTree(TNode T[])
{
int N;
scanf("%d",&N);//输入一个N,接下来输入N行结点数据
getchar()//读取回车
char data,lc,rc;
int Root = NULL;//将根节点变量初始化为NULL,如果不初始化,树空时返回Root会发生错误
int JudgeRoot[N];
for(int i=0;i<N;i++)
JudgeRoot[i]=0;
for(int i=0;i<N;i++)
{
scanf("%c %c %c",&data,&lc,&rc);//输入结点的三个数据,暂存于data,lc,rc中
//lc,rc类型为char是因为,lc,rc可能是'-'
getchar();//读取回车
T[i].data=data//将data存储的数据赋值给结点中的data
if(lc!='-')
{
T[i].lchild = lc-'0'; //因为一开始输入是以char类型输入的,所以要将其转化为int类型
JudgeRoot[T[i].lchild]=1;//将该结点左孩子位置设为1,代表该位置存放的不是根节点
}
else
T[i].lchild = NULL;//注意这里的left和right不是指针,而是游标。所以当他们都指向空的
//时候,NULL可不是0,而是-1(c++规定空指针=0.而为了区分这里把NULL设为-1)
//同理右孩子也是如此
if(rc!='-')
{
T[i].rchild = rc-'0'; //因为一开始输入是以char类型输入的,所以要将其转化为int类型
JudgeRoot[T[i].rchild]=1;//将该结点左孩子位置设为1,代表该位置存放的不是根节点
}
else
T[i].rchild = NULL;
}
for(int i=0;i<N;i++)
{
if(!JudgeRoot[i])
{
Root=i;
break;
}
}
return Root;
}
同构的判断
判断同构的思想还是用递归的思想,即递归判断两棵树的左右子树是不是同构
判断同构需要考虑多种情况
1.都为空树;
2.一个为树为空,一个不为空
3.两个树都不为空
其中两个树都不为空还包括,左右子树是否为空,两个树的左右子树的同构判断需不需要换位
首先设置一个判断同构的函数,该函数返回值类型为布尔值类型,同构返回true,否则返回false,设这个函数名为bool Ifisomorphism(TNode Tree1[], int root1, TNode Tree2[], int root2)
下面为代码实现
bool Ifisomorphism(TNode Tree1[], int root1, TNode Tree2[], int root2)
{
if(root1==NULL&&root2==NULL)
return true;//如果两个树都为空, 返回true
if((root1==NULL&&root2!=NULL)||(root1!=NULL&&root2==NULL))
return false;//如果两个树,一个为空,一个不为空,返回false
if(Tree1[root1].data!=Tree2[root2].data)
return false;//如果两个树的根结点不相等,返回false
else{//如果根节点相等
if(Tree1[root1].lchild==NULL&&Tree22[root2].lchild==NULL)
//如果两个树左子树都为空,那么比较两棵树的右子树
return Ifisomorphism(Tree1,Tree1[root1].rchild,Tree2,Tree2[root2].rchild);
else if(Tree1[root1].rchild==NULL&&Tree2[root2].rchild==NULL)
//若果两棵树右子树都为空,那么比较两棵树的左子树
return Ifisomorphism(Tree1,Tree1[root1].lchild,Tree2,Tree2[root2].lchild);
else if(Tree1[root1].lchild==NULL&&Tree2[root2].rchild==NULL)
//如果树一的左子树为空,树二的右子树为空,那么比较树一的右子树和树二的左子树
return Ifisomorphism(Tree1,Tree1[root1].rchild,Tree2,Tree2[root2].lchild);
else if(Tree[root1].rchild==NULL&&Tree2[root2].lchild==NULL)
//如果树一的右子树为空,树二的左子树为空,那么比较树一的左子树和树二的右子树
return Ifisomorphism(Tree1,Tree1[root1].lchild,Tree2,Tree2[root2].rchild);
else{//若果两棵子树都不为空
if(Tree1[Tree1[root1].lchild].data==Tree2[Tree2[root2].lchild].data)
//两棵树的左子树根结点相等,那么不换位,直接左子树比较左子树,右子树比较右子树
return Ifisomorphism(Tree1,Tree1[root1].lchild,Tree2,Tree2[root2].lchild)&&Ifisomorphism(Tree1,Tree1[root1].rchild,Tree2,Tree2[root2].rchild);
else
//如果不是,即两棵树的左子树根结点不相等,那么换位,左子树比较右子树,右子树比较左子树
return Ifisomorphism(Tree1,Tree1[root1].lchild,Tree2,Tree2[root2].rchild)&&Ifisomorphism(Tree1,Tree1[root1].rchild,Tree2,Tree2[root2].lchild);
}
}
}
综上所述,本题的完整代码:
#include <bits/stdc++.h>
#define NULL -1
using namespace std;
typedef struct{
char data;
int lchild;
int rchild;
} TNode;
int CreatTree(TNode T[]);
bool Ifisomorphism(TNode Tree1[], int root1, TNode Tree2[], int root2);
int main()
{
TNode Tree1[10];//创建二叉树Tree1,因为N最大为10,所以结点最多为10个
TNode Tree2[10];//创建二叉树TREE2
//创建完树以后,将N个结点顺序输入到树种,并找出根结点所在位置
//CreatTree(TNode T[])函数,实现读取数据,并返回根节点位置的功能
int root1 = CreatTree(Tree1);//int类型的root1变量,代表Tree1中根结点的位置
int root2 = CreatTree(Tree2);//int类型的root2变量,代表Tree2中根结点的位置
//调用Ifisomorphism()进行同构判断;
bool Judge = Ifisomorphism(Tree1[], root1, Tree2[], root2);
if(Judge)
printf("Yes");
else
printf("No");
return 0;
}
int CreatTree(TNode T[])
{
int N;
scanf("%d",&N);//输入一个N,接下来输入N行结点数据
getchar()//读取回车
char data,lc,rc;
int Root = NULL;//将根节点变量初始化为NULL,如果不初始化,树空时返回Root会发生错误
int JudgeRoot[N];
for(int i=0;i<N;i++)
JudgeRoot[i]=0;
for(int i=0;i<N;i++)
{
scanf("%c %c %c",&data,&lc,&rc);//输入结点的三个数据,暂存于data,lc,rc中
//lc,rc类型为char是因为,lc,rc可能是'-'
getchar();//读取回车
T[i].data=data//将data存储的数据赋值给结点中的data
if(lc!='-')
{
T[i].lchild = lc-'0'; //因为一开始输入是以char类型输入的,所以要将其转化为int类型
JudgeRoot[T[i].lchild]=1;//将该结点左孩子位置设为1,代表该位置存放的不是根节点
}
else
T[i].lchild = NULL;//注意这里的left和right不是指针,而是游标。所以当他们都指向空的
//时候,NULL可不是0,而是-1(c++规定空指针=0.而为了区分这里把NULL设为-1)
//同理右孩子也是如此
if(rc!='-')
{
T[i].rchild = rc-'0'; //因为一开始输入是以char类型输入的,所以要将其转化为int类型
JudgeRoot[T[i].rchild]=1;//将该结点左孩子位置设为1,代表该位置存放的不是根节点
}
else
T[i].rchild = NULL;
}
for(int i=0;i<N;i++)
{
if(!JudgeRoot[i])
{
Root=i;
break;
}
}
return Root;
}
bool Ifisomorphism(TNode Tree1[], int root1, TNode Tree2[], int root2)
{
if(root1==NULL&&root2==NULL)
return true;//如果两个树都为空, 返回true
if((root1==NULL&&root2!=NULL)||(root1!=NULL&&root2==NULL))
return false;//如果两个树,一个为空,一个不为空,返回false
if(Tree1[root1].data!=Tree2[root2].data)
return false;//如果两个树的根结点不相等,返回false
else{//如果根节点相等
if(Tree1[root1].lchild==NULL&&Tree22[root2].lchild==NULL)
//如果两个树左子树都为空,那么比较两棵树的右子树
return Ifisomorphism(Tree1,Tree1[root1].rchild,Tree2,Tree2[root2].rchild);
else if(Tree1[root1].rchild==NULL&&Tree2[root2].rchild==NULL)
//若果两棵树右子树都为空,那么比较两棵树的左子树
return Ifisomorphism(Tree1,Tree1[root1].lchild,Tree2,Tree2[root2].lchild);
else if(Tree1[root1].lchild==NULL&&Tree2[root2].rchild==NULL)
//如果树一的左子树为空,树二的右子树为空,那么比较树一的右子树和树二的左子树
return Ifisomorphism(Tree1,Tree1[root1].rchild,Tree2,Tree2[root2].lchild);
else if(Tree[root1].rchild==NULL&&Tree2[root2].lchild==NULL)
//如果树一的右子树为空,树二的左子树为空,那么比较树一的左子树和树二的右子树
return Ifisomorphism(Tree1,Tree1[root1].lchild,Tree2,Tree2[root2].rchild);
else{//若果两棵子树都不为空
if(Tree1[Tree1[root1].lchild].data==Tree2[Tree2[root2].lchild].data)
//两棵树的左子树根结点相等,那么不换位,直接左子树比较左子树,右子树比较右子树
return Ifisomorphism(Tree1,Tree1[root1].lchild,Tree2,Tree2[root2].lchild)&&Ifisomorphism(Tree1,Tree1[root1].rchild,Tree2,Tree2[root2].rchild);
else
//如果不是,即两棵树的左子树根结点不相等,那么换位,左子树比较右子树,右子树比较左子树
return Ifisomorphism(Tree1,Tree1[root1].lchild,Tree2,Tree2[root2].rchild)&&Ifisomorphism(Tree1,Tree1[root1].rchild,Tree2,Tree2[root2].lchild);
}
}
}
总结:这篇代码也不是我自己想出来的,而是参考了好多优秀的代码,加上自己的理解得来的,加油吧!