PTA_2019春_031_树的同构

博客探讨了如何判断两棵二叉树是否同构,同构定义为通过左右孩子互换可以相互转换。文章提供了输入输出格式及样例,并指出在实现过程中需要注意的细节,如结构体表示树、避免越界问题等。作者分享了遇到的问题和解决方案,并展示了一段可能存在问题的代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给定两棵树T1和T2。如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是“同构”的。例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A、B、G的左右孩子互换后,就得到另外一棵树。而图2就不是同构的。

 

 

图1

图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.用结构体来实现树。

2.思路:对于任意一个节点来说,它的子节点并没有改变,或者只是左右子节点互换位置
否则不是同构

3.注:由于是通过结构来实现,即访问子节点是通过坐标,而题中所给空节点为“-”,将“-”换成-1存入

而对于数组来说-1是越界的,所以要注意。

这是经过同学修改的,虽然在PTA里可以跑满分,但是还是有点问题的(越界),具体问题见代码注释处。

/*判断是否同构*/
/*思路:对于任意一个节点来说,它的子节点并没有改变,或者只是左右子节点互换位置
否则不是同构*/
#include<stdio.h> 
#include<stdlib.h>

typedef struct node {
	char key;
	int left;
	int right;
}Tree;
int Find(Tree* p, int number, char key);
int main() {
	/*初始化*/
	int number1, number2;/*两个树的节点数*/
	int nullp1, nullp2;/*用于scanf返回值,以判断输入是否为空指针“-”*/
	scanf("%d", &number1);

	//Tree tree1[number1];/*树一,用结构体数组表达*/
<think>嗯,用户想解决PTA平台上同构问题的Python实现。首先,我需要回忆一下同构的定义。根据引用[3],同构可以通过交换某些节点的左右子来变得结构相同。比如图1中的例子,交换A、B、G的左右孩子后,两棵结构相同。而图2则不行。所以判断同构的关键在于递归比较根节点的值和子结构是否匹配,包括可能交换左右子的情况。 接下来,用户需要Python的解题思路和代码实现。首先,我得考虑输入的处理。根据引用[2],输入包括每个节点的字母、左孩子和右孩子的编号。对于每个,先读取n,然后n行数据。节点编号是0到n-1,空孩子用“-”表示。所以需要将这些输入转换为节点对象或字典来存储每个节点的左右子节点。 然后,如何找到的根节点呢?通常根节点没有父节点,所以可以通过遍历所有节点,找出没有出现在任何子节点位置的编号。比如,用一个集合记录所有子节点,剩下的那个就是根节点。 接下来,核心算法是递归判断两棵是否同构。根据引用[1],核心在于递归比较。首先,如果两棵都为空,返回True。如果一个空一个不空,返回False。然后,比较当前两个根节点的值是否相同。如果不同,直接False。接着,递归判断两种情况:不交换左右子的情况,或者交换其中一个的左右子的情况。只要有一种情况满足即可。 那具体的步骤应该是这样的:对于两棵的当前节点,比较它们的值。然后检查它们的左右子是否相同,或者是否在交换左右后相同。比如,1的左子2的左子是否同构,同时右子同构;或者1的左子2的右子同构,同时右子2的左子同构。这两种情况满足其一即可。 需要注意边界条件,比如当其中一棵子为空的情况。例如,如果1的左子为空,而2的右子也为空,那么交换后可能匹配。 然后,代码实现部分。需要将输入转换为节点结构。可以用字典来存储每个节点的左右孩子。例如,对于每个节点,存储其左孩子和右孩子的编号,如果是“-”则标记为None或者-1。然后,找到根节点后,递归判断。 例如,构建的结构时,可以用一个列表或字典,每个节点存储左和右子节点的索引。例如,nodes1是一个字典,键是节点编号,值是一个包含左和右子节点的元组。然后,递归函数需要接收两个当前节点的编号,以及对应的两棵的节点字典。 还要处理节点值为空的情况,比如当某个节点不存在时,需要返回对应的判断。 代码的大致流程: 1. 读取输入,构建两棵的节点结构。 2. 找到两棵的根节点。 3. 使用递归函数判断是否同构。 举个例子,假设输入的结构如下: 1: 3 A 1 2 B - - C - - 2: 3 A 2 1 B - - C - - 这两棵的根节点都是0号节点。递归比较时,根节点的值相同,然后比较左左和右右是否同构,或者左右和右左是否同构。这时候,交换左右子后,可以得到相同结构,所以返回True。 在Python中,如何处理节点的读取呢?可以用一个函数来读取n行输入,每个节点存储字母、左、右。例如: def read_tree(): n = int(input()) nodes = {} children = set() for i in range(n): parts = input().split() letter = parts[0] left = parts[1] if parts[1] != '-' else -1 right = parts[2] if parts[2] != '-' else -1 if left != -1: left = int(left) children.add(left) if right != -1: right = int(right) children.add(right) nodes[i] = (letter, left, right) # 找根节点,即没有在children中的节点 root = None for i in range(n): if i not in children: root = i break return root, nodes 然后,递归函数: def is_isomorphic(root1, root2, nodes1, nodes2): # 都为空 if root1 == -1 and root2 == -1: return True # 一个空,另一个不空 if (root1 == -1) != (root2 == -1): return False # 节点值不同 node1 = nodes1[root1] node2 = nodes2[root2] if node1[0] != node2[0]: return False # 比较不交换的情况和交换的情况 return (is_isomorphic(node1[1], node2[1], nodes1, nodes2) and is_isomorphic(node1[2], node2[2], nodes1, nodes2)) or \ (is_isomorphic(node1[1], node2[2], nodes1, nodes2) and is_isomorphic(node1[2], node2[1], nodes1, nodes2)) 这样可能会有问题,比如,当两个结构不同,但交换后可能匹配。例如,左子和右子交换后是否满足条件。 但需要考虑节点为空的情况,所以在递归调用时,需要处理当某个子节点为-1的情况。 然后,测试这个函数。例如,当根节点的左右子交换后,是否返回True。 可能需要注意,当两个结构在交换后必须同时满足同构。例如,左子和对方的右子同构,同时右子和对方的左子同构。 此外,还需要处理当两个的结构不同,但字母相同的情况下是否能够正确判断。 可能存在的错误情况:比如,节点值相同,但子结构无法匹配,即使交换左右。 例如,1的根A有左子B和右子C,而2的根A有左子C和右子B。此时交换后是否结构相同? 如果B和C的结构不同,那么可能无法匹配。 另一个情况是,1的根A的左子是B,而2的根A的右子是B,但B的结构不同,这时候应该返回False。 所以递归必须严格比较子的结构。 最后,将上述思路整理成代码,并处理输入输出。 可能的测试用例: 输入两棵相同的,返回Yes;输入交换左右子,返回Yes;输入结构不同的,返回No。 例如,引用[3]中的例子,如果两棵在交换某些节点的子后结构相同,则返回Yes。 所以,最终的Python代码大致结构应该是这样的: 读取两棵的结构,找到根节点,然后调用递归函数判断是否同构。 需要注意的是,Python中如何处理节点的编号,特别是在转换输入时,是否需要将字符串转换为整数,比如将“-”转换为-1。 此外,如果某个为空(n=0),如何处理?题目中输入的n是非负整数,所以当n=0时,没有节点。此时如果另一棵也是n=0,则同构;否则不同构。 综上,完整的Python代码可能如下: 读取两棵的结构,处理根节点的查找,然后递归判断。 现在,将这些思路转化为具体的代码步骤,并确保正确处理所有边界情况。</think>### PTA同构问题Python解题思路 #### 问题分析 同构指两棵通过交换某些节点的左右子后结构相同[^3]。解决该问题的核心在于递归比较两棵的结构是否匹配,允许左右子交换。 #### 输入处理 1. **输入格式**:每棵输入包含节点数$n$,随后$n$行描述节点的字母、左孩子和右孩子的编号(空为`-`)[^2]。 2. **数据结构**:使用字典存储每个节点的左/右孩子编号,如`nodes1[i] = (val, left, right)`。 #### 算法步骤 1. **寻找根节点**:根节点是没有出现在任何子节点位置的编号。 2. **递归判断同构**: - 若两棵均为空,返回`True`。 - 若一棵空另一非空,返回`False`。 - 若当前节点字母不同,返回`False`。 - 递归比较两种情况: 1. **不交换左右子**:比较左子与左子、右子与右子。 2. **交换左右子**:比较左子与右子、右子与左子。 #### 代码实现 ```python def read_tree(): n = int(input()) if n == 0: return -1, {} # 空处理 nodes = {} children = set() for _ in range(n): val, left, right = input().split() left = int(left) if left != '-' else -1 right = int(right) if right != '-' else -1 nodes[_] = (val, left, right) if left != -1: children.add(left) if right != -1: children.add(right) root = -1 for i in range(n): if i not in children: root = i break return root, nodes def is_isomorphic(root1, root2, tree1, tree2): # 两均为空 if root1 == -1 and root2 == -1: return True # 一棵空另一非空 if (root1 == -1) ^ (root2 == -1): return False # 当前节点值不同 node1 = tree1[root1] node2 = tree2[root2] if node1[0] != node2[0]: return False # 不交换左右子的情况 case1 = is_isomorphic(node1[1], node2[1], tree1, tree2) and \ is_isomorphic(node1[2], node2[2], tree1, tree2) # 交换左右子的情况 case2 = is_isomorphic(node1[1], node2[2], tree1, tree2) and \ is_isomorphic(node1[2], node2[1], tree1, tree2) return case1 or case2 # 主程序 root1, tree1 = read_tree() root2, tree2 = read_tree() if is_isomorphic(root1, root2, tree1, tree2): print("Yes") else: print("No") ``` #### 代码说明 1. **`read_tree`函数**:读取输入并构建结构,返回根节点和节点字典。 2. **`is_isomorphic`函数**:递归判断两棵是否同构,允许左右子交换。 3. **边界处理**:空、节点值不同、子结构不匹配等情况。 #### 示例输入 ```text 3 A 1 2 B - - C - - 3 A 2 1 B - - C - - ``` 输出:`Yes`(交换左右子后结构相同)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值