1. 程式人生 > >樹的同構(25 分)[有點難度的遞迴]

樹的同構(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

本題精髓在於遞迴部分,初看有點難想出。

#include <iostream>
#include <cstring>
#define MAXSIZE 10

//使用靜態連結串列,-1表示null
struct
TreeNode { char data; int cl; int cr; } t1[MAXSIZE], t2[MAXSIZE]; int CreateTree(struct TreeNode t[]) { int N, root; std::cin >> N; if(!N) return -1; int check[N]; memset(check, 0, sizeof(check)); for(int i=0; i<N; ++i) { char tcl, tcr; std
::cin >> t[i].data >> tcl >> tcr; if(tcl!='-') { t[i].cl = tcl-'0'; check[t[i].cl] = 1; } else t[i].cl = -1; if(tcr!='-') { t[i].cr = tcr-'0'; check[t[i].cr] = 1; } else t[i].cr = -1; } int index; for(index=0; index<N; ++index) { if(!check[index]) break; } root = index; return root; } bool isSame(int r1, int r2) { if(r1==-1 && r2==-1) return true; //有一個是-1,才會導致-1,這樣就是非同構 if(r1*r2 < 0) return false; //“動態根節點”不相等,false if(t1[r1].data != t2[r2].data) return false; //“動態根節點”都不存在,去看右邊,同時左邊不需要看了 if(t1[r1].cl==-1 && t2[r2].cl==-1) return isSame(t1[r1].cr, t2[r2].cr); //“動態根節點”都有左孩子,且相等,這時候得把遞迴, //把此時的子都變成引數中的“根”,左右“同時”看 if((t1[r1].cl!=-1 && t2[r2].cl!=-1 )&& (t1[t1[r1].cl].data == t2[t2[r2].cl].data)) return (isSame(t1[r1].cl, t2[r2].cl) && isSame(t1[r1].cr, t2[r2].cr)); //這時候,一種是兩邊都有但不相等,所以“swap”,看左右,右左的關係; //二是一邊沒有一邊有,這裡和之前不一樣,這裡還沒確定“動態根節點” else return (isSame(t1[r1].cl, t2[r2].cr) && isSame(t1[r1].cr, t2[r2].cl)); } int main() { int r1, r2; //兩個樹(兩個根) r1 = CreateTree(t1); r2 = CreateTree(t2); if(isSame(r1, r2)) std::cout << "Yes\n"; else std::cout << "No\n"; return 0; }