二叉树同构问题
题目
给定两棵树T1和T2。如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是“同构”的。
现给定两棵树,请你判断他们是否是同构的。
例如:
同构的树:
不是同构的树:
输入
输入格式:输入两棵二叉树的信息
- 先在一行中给出该树的结点数N;
- 输入N行,第i行对应编号第i个结点,给出该结点中存储的字母、其左孩子结点的编号、右孩子结点的编号;(结点可以按照任意顺序排列)
- 如果孩子结点为空,则在相应位置上给出"-"。
输入样例:
分别对应的二叉树:
求解思路
二叉树的表示
本题用结构数组表示二叉树(静态链表)
#define MAXSIZE 20
#define ElementType char
#define Tree int
#define bool int
#define Null -1//注意不是NULL
//定义两个静态链表T1、T2,未赋值
struct TreeNode
{
ElementType Element;
Tree Left;
Tree Right;
}T1[MAXSIZE],T2[MAXSIZE];
Element
存储结点信息;Left
存储左儿子在数组中位置的下标;Right
储存右儿子在数组中位置的下标(-1表示没有儿子)。
例如:
程序框架
int main(void)
{
建二叉树1;
建二叉树2;
判断是否同构并输出;
return 0;
}
需要设计的函数:
- 读数据建二叉树:
Tree BuildTree(struct TreeNode [])
- 二叉树同构判别:
bool Isomorphic(Tree, Tree)
所以程序框架:
int main(void)
{
Tree R1 = 0, R2 = 0;
R1 = BuildTree(T1);//读入输入的数据,并建立静态链表R1
R2 = BuildTree(T2);//读入输入的数据,并建立静态链表R2
//判断是否同构,同构打印Yes,否则打印No
if (Isomorphic(R1, R2))
{
printf("Yes\n");
}
else
{
printf("No\n");
}
system("pause");
return 0;
}
如何建立二叉树
主要难点在于找出二叉树的根结点:
根结点与其他结点的区别在于没有父结点,所以记录结构数组中每个元素的左右子树指向的位置,最后始终没有被指中的就是根结点。
详细见代码中check[]
数组。
如何判断两二叉树是否同构
运用递归的思路,将二叉树一步步分解成最小的树。
详细见代码中Isomorphic(R1, R2)
函数。
代码示例
main.c文件
#include<stdio.h>
#include<stdlib.h>
#include"check.h"
int main(void)
{
Tree R1 = 0, R2 = 0;
R1 = BuildTree(T1);//读入输入的数据,并建立静态链表R1
R2 = BuildTree(T2);//读入输入的数据,并建立静态链表R2
//判断是否同构,同构打印Yes,否则打印No
if (Isomorphic(R1, R2))
{
printf("Yes\n");
}
else
{
printf("No\n");
}
system("pause");
return 0;
}
check.h文件
#define MAXSIZE 20
#define ElementType char
#define Tree int
#define bool int
#define Null -1//注意不是NULL
//定义两个静态链表T1、T2,未赋值
struct TreeNode
{
ElementType Element;
Tree Left;
Tree Right;
}T1[MAXSIZE],T2[MAXSIZE];
Tree BuildTree(struct TreeNode []);
bool Isomorphic(Tree, Tree);
BuildTree.c文件
#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
#include<stdlib.h>
#include"check.h"
Tree BuildTree(struct TreeNode T[])
{
int N = 0, Root = Null;
int i = 0;
char cl, cr;
int *check = NULL;
scanf("%d", &N);//读入结点数
getchar();//吃掉缓冲区的回车
if (N)
{
//check[]数组用于标记哪个结点没有父结点,没有的就是二叉树的根,全部初始化为0
check = (int *)malloc(sizeof(int)*N);
for (i = 0; i < N; i++)
{
check[i] = 0;
}
//读入输入的结点信息并加以处理
for (i = 0; i < N; i++)
{
scanf("%c %c %c", &T[i].Element, &cl, &cr);
getchar();//吃掉缓冲区的回车
//将字符cl转化为数字,并标记有父结点的结点,对应的check[i]=1
if (cl != '-')
{
T[i].Left = cl - '0';
check[T[i].Left] = 1;
}
else
{
T[i].Left = Null;
}
//将字符cr转化为数字,并标记有父结点的结点,对应的check[i]=1
if (cr != '-')
{
T[i].Right = cr - '0';
check[T[i].Right] = 1;
}
else
{
T[i].Right = Null;
}
}
//找出check[]数组中为0(没有父结点的结点)的位置,此位置就是根在数组T[]中的位置
for (i = 0; i < N; i++)
{
if (check[i] == 0)
{
break;
}
}
Root = i;
}
free(check);//释放空间
return Root;
}
Isomorphic.c文件
#include<stdio.h>
#include<stdlib.h>
#include"check.h"
bool Isomorphic(Tree R1, Tree R2)
{
//两棵二叉树都是空树的情况
if (R1 == Null && R2 == Null)
{
return 1;
}
//两棵二叉树一个是空树,另一个不是的情况
else if ((R1 == Null && R2 != Null) || (R1 != Null && R2 == Null))
{
return 0;
}
//根结点不一样的情况
else if (T1[R1].Element != T2[R2].Element)
{
return 0;
}
//当 树1的左子树与树2的左子树同构 并且 树1的右子树与树2的右子树同构 或者 树1的左子树与树2的右子树同构 并且 树1的右子树与树2的左子树同构时,才算是同构
else
{
return ((Isomorphic(T1[R1].Left, T2[R2].Left) && Isomorphic(T1[R1].Right, T2[R2].Right)) || (Isomorphic(T1[R1].Left, T2[R2].Right) && Isomorphic(T1[R1].Right, T2[R2].Left)));
}
}