樹的同構:
給定兩棵樹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 }
