第六章树和二叉树--树和森林-计算机17级 7-1 树的同构 (25 分)

本文介绍了一种判断两棵树是否同构的算法,通过左右孩子互换来判断两棵树是否可以互相转换。文章详细解释了算法的实现过程,包括二叉树的结构定义、创建方法及同构判断的具体步骤。

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

题意理解:
  1. 先在一行输入一个N,下面有N行输入
  2. 每一行对应一个结点,给出该结点储存的数据,左孩子编号,右孩子编号,如果孩子结点为空,那么就输入一个“-”
程序大致框架:

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);
        }
	}
}

总结:这篇代码也不是我自己想出来的,而是参考了好多优秀的代码,加上自己的理解得来的,加油吧!

1. 一棵二叉树的顺序存储情况如下: 中,度为2的结点数为( )。 A.1 B.2 C.3 D.4 2. 一棵“完全二叉树”结点数为25,高度为( )。 A.4 B.5 C.6 D.不确定 3.下列说法中,( )是正确的。 A. 二叉树就是度为2的 B. 二叉树中不存在度大于2的结点 C. 二叉树是有序 D. 二叉树中每个结点的度均为2 4.一棵二叉树的前序遍历序列为ABCDEFG,它的中序遍历序列可能是( )。 A. CABDEFG B. BCDAEFG C. DACEFBG D. ADBCFEG 5.线索二叉树中的线索指的是( )。 A.左孩子 B.遍历 C.指针 D.标志 6. 建立线索二叉树的目的是( )。 A. 方便查找某结点的前驱或后继 B. 方便二叉树的插入与删除 C. 方便查找某结点的双亲 D. 使二叉树的遍历结果唯一 7. 有abc三个结点的右单枝二叉树的顺序存储结构应该用( )示意。 A. a b c B. a b ^ c C. a b ^ ^ c D. a ^ b ^ ^ ^ c 8. 一颗有2046个结点的完全二叉树的第10层上共有( )个结点。 A. 511 B. 512 C. 1023 D. 1024 9. 一棵完全二叉树一定是一棵( )。 A. 平衡二叉树 B. 二叉排序 C. 堆 D. 哈夫曼 10.某二叉树的中序遍历序列后序遍历序列正好相反,则该二叉树一定是( )的二叉树。 A.空或只有一个结点 B.高度等于其结点数 C.任一结点无左孩子 D.任一结点无右孩子 11.一棵二叉树的顺序存储情况如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 A B C D E 0 F 0 0 G H 0 0 0 X 结点D的左孩子结点为( )。 A.E B.C C.F D.没有 12.一棵“完全二叉树”结点数为25,高度为( )。 A.4 B.5 C.6 D.不确定 二、填空题(每空3,共18)。 1. 的路径长度:是从根到每个结点的路径长度之。对结点数相同的来说,路径长度最短的是 完全 二叉树。 2. 在有n个叶子结点的哈夫曼中,总结点数是 2n-1 。 3. 在有n个结点的二叉链表中,值为非空的链域的个数为 n-1 。 4. 某二叉树的中序遍历序列后序遍历序列正好相反,则该二叉树一定是 任一结点无左孩子 的二叉树。 5. 深度为 k 的二叉树最多有 个结点,最少有 k 个结点。 三、综合题(共58)。 1. 假定字符集{a,b,c,d,e,f }中的字符在电码中出现的次数如下: 字符 a b c d e f 频度 9 12 20 23 15 5 构造一棵哈夫曼(6),给出每个字符的哈夫曼编码(4),并计算哈夫曼的加权路径长度WPL(2)。 (符合WPL最小的均为哈夫曼,答案不唯一) 哈夫曼编码: 2. 假设用于通信的电文由字符集{a,b,c,d,e,f,g}中的字符构成,它们在电文中出现的频率别为{0.31,0.16,0.10,0.08,0.11,0.20,0.04}。要求: (1)为这7个字符设计哈夫曼(6)。 (2)据此哈夫曼设计哈夫曼编码(4)。 (3)假设电文的长度为100字符,使用哈夫曼编码比使用3位二进制数等长编码使电文总长压缩多少?(4(1) 为这7个字符设计哈夫曼为(符合WPL最小的均为哈夫曼,答案不唯一): (2) 哈夫曼编码为: a:01;b:001;c:100;d:0001;e:101;f:11;g:0000 (3) 假设电文的长度为100字符,使用哈夫曼编码比使用3位二进制数等长编码使电文总长压缩多少? 采用等长码,100个字符需要300位二进制数,采用哈夫曼编码发送这100个字符需要261二进制位,压缩了300-261=39个字符。压缩比为39/300=13%。 3. 二叉数T的(双亲到孩子的)边集为: { <A,B>, <A,C>, <D,A>, <D,E>, <E,F>, <F,G> } 请回答下列问题: (1)T的根结点(2): (2)T的叶结点(2): (3)T的深度(2): (4)如果上述列出边集中,某个结点只有一个孩子时,均为其左孩子;某个结点有两个孩子时,则先列出了连接左孩子的边后列出了连接右孩子的边。画出该二叉树其及前序线索(6)。 (1)T的根结点 (2)T的叶结点 : (3)T的深度 : (4)二叉树其及前序线索为: 4.现有以下按前序中序遍历二叉树的结果: 前序:ABCEDFGHI 中序:CEBGFHDAI 画出该二叉树的逻辑结构图(5),并在图中加入中序线索(5)。 5.有电文:ABCDBCDCBDDBACBCCFCDBBBEBB。 用Huffman构造电文中每一字符的最优通讯编码。画出构造的哈夫曼,并给出每个字符的哈夫曼编码方案。(符合WPL最小的均为哈夫曼,答案不唯一) (1)构造哈夫曼(6): (2)哈夫曼编码方案(4):
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值