树的同构:
给定两棵树T1和T2。如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是“同构”的。例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A、B、G的左右孩子互换后,就得到另外一棵树。而图2就不是同构的。
图1
图2
现给定两棵树,请你判断它们是否是同构的。
输入格式:
输入给出2棵二叉树树的信息。对于每棵树,首先在一行中给出一个非负整数N (≤),即该树的结点数(此时假设结点从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
本题使用结构数组(静态链表)存储二叉树
结构定义:
#define MaxTree 10 #define ElementType char #define Tree int #define Null -1 struct TreeNode { ElementType Element; Tree Left; Tree Right; }T1[MaxTree], T2[MaxTree];
每个结点包含一个值和左右节点在数组中的位置
程序整体结构:
1、根据输入,构建二叉树
int BuildTree(struct TreeNode T[]) { int N, i, root = Null; char cl, cr; scanf("%d", &N); if(N){ int check[MaxTree];//用于判断根节点 for(i = 0; i < N; i++){ check[i] = 0; } for (i = 0; i < N; i++){ getchar(); scanf("%c %c %c", &T[i].Element, &cl, &cr); if (cl != '-'){ T[i].Left = cl - '0'; check[T[i].Left] = 1; }else{ T[i].Left = Null; } if (cr != '-'){ T[i].Right = cr - '0'; check[T[i].Right] = 1; } else{ T[i].Right = Null; } } for (i = 0; i < N; i++){ if (0 == check[i]){ root = i; break; } } } return root; }
要注意的是,要将换行过滤,避免存入结构数组里
getchar();
scanf("%c %c %c", &T[i].Element, &cl, &cr);
如何判断根节点?
未被指向过的节点就是根节点(节点存在,但对应的数组下标从未出现在其他节点的Left和Right
对应程序,使用check数组变量:
1、数组长度和节点个数一样,初始化为0
2、一旦某个节点有指向信息,chenck数组对应下标处置1
输入全部处理完成后,依然保持0的就是从未被指向过的节点,就是想要的根节点
2、判断二叉树同构:
int Isomorphic(Tree R1, Tree R2) { if ((R1 == Null) && (R2 == Null)){ return 1; } //对应一个树存在某个节点,而另一个树没有该节点 else if ((R1 == Null && R2 != Null) || (R2 == Null && R1 != Null)){ return 0; } else if (T1[R1].Element != T2[R2].Element) { return 0; } else if (Isomorphic(T1[R1].Left, T2[R2].Left) && Isomorphic(T1[R1].Right, T2[R2].Right)) { return 1; } else if (Isomorphic(T1[R1].Left, T2[R2].Right) && Isomorphic(T1[R1].Right, T2[R2].Left)){ return 1; } return 0; }
逻辑判断要从大处着眼
只要R1和R2不为空,就去判断它们的左右子树是否同构,不需要多此一举的判断R1、R2的左又子节点是否为空,(子节点是否为空自然会在下一次递归调用中判断),递归要做到只关心当次循环
完整程序:

1 #include<iostream> 2 using std::cout; 3 using std::endl; 4 5 #define MaxTree 10 6 #define ElementType char 7 #define Tree int 8 #define Null -1 9 10 struct TreeNode 11 { 12 ElementType Element; 13 Tree Left; 14 Tree Right; 15 }T1[MaxTree], T2[MaxTree]; 16 17 /* 18 8 19 A 1 2 20 B 3 4 21 C 5 - 22 D - - 23 E 6 - 24 G 7 - 25 F - - 26 H - - 27 8 28 G - 4 29 B 7 6 30 F - - 31 A 5 1 32 H - - 33 C 0 - 34 D - - 35 E 2 - 36 */ 37 38 39 int BuildTree(struct TreeNode T[]) 40 { 41 int N, i, root = Null; 42 char cl, cr; 43 scanf("%d", &N); 44 if(N){ 45 int check[MaxTree];//用于判断根节点 46 for(i = 0; i < N; i++){ 47 check[i] = 0; 48 } 49 50 51 for (i = 0; i < N; i++){ 52 getchar(); 53 scanf("%c %c %c", &T[i].Element, &cl, &cr); 54 if (cl != '-'){ 55 T[i].Left = cl - '0'; 56 check[T[i].Left] = 1; 57 }else{ 58 T[i].Left = Null; 59 } 60 61 if (cr != '-'){ 62 T[i].Right = cr - '0'; 63 check[T[i].Right] = 1; 64 } 65 else{ 66 T[i].Right = Null; 67 } 68 } 69 70 71 for (i = 0; i < N; i++){ 72 if (0 == check[i]){ 73 root = i; 74 break; 75 } 76 } 77 } 78 return root; 79 } 80 81 82 int Isomorphic(Tree R1, Tree R2) 83 { 84 if ((R1 == Null) && (R2 == Null)){ 85 return 1; 86 } 87 88 //对应一个树存在某个节点,而另一个树没有该节点 89 else if ((R1 == Null && R2 != Null) || (R2 == Null && R1 != Null)){ 90 return 0; 91 } 92 93 else if (T1[R1].Element != T2[R2].Element) 94 { 95 return 0; 96 } 97 98 else if (Isomorphic(T1[R1].Left, T2[R2].Left) && Isomorphic(T1[R1].Right, T2[R2].Right)) { 99 return 1; 100 } 101 else if (Isomorphic(T1[R1].Left, T2[R2].Right) && Isomorphic(T1[R1].Right, T2[R2].Left)){ 102 return 1; 103 } 104 105 return 0; 106 } 107 108 int main() 109 { 110 freopen("data.txt", "r", stdin);//输入重定向,方便调试 111 Tree r1, r2; 112 113 r1 = BuildTree(T1); 114 r2 = BuildTree(T2); 115 if (Isomorphic(r1, r2)){ 116 cout<< "Yes"<<endl ; 117 } 118 else 119 { 120 cout << "No" << endl; 121 } 122 123 return 0; 124 }