樹的同構(25 分)[有點難度的遞迴]
阿新 • • 發佈:2019-02-05
樹的同構
給定兩棵樹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;
}