7-1 樹的同構 (30 point(s))
給定兩棵樹T1和T2。如果T1可以通過若干次左右孩子互換就變成T2,則我們稱兩棵樹是“同構”的。例如圖1給出的兩棵樹就是同構的,因為我們把其中一棵樹的結點A、B、G的左右孩子互換后,就得到另外一棵樹。而圖2就不是同構的。

圖1
圖2
輸入格式:
輸入給出2棵二叉樹樹的信息。對於每棵樹,首先在一行中給出一個非負整數N (≤),即該樹的結點數(此時假設結點從0到N−1編號);隨后N行,第i行對應編號第i個結點,給出該結點中存儲的1個英文大寫字母、其左孩子結點的編號、右孩子結點的編號。如果孩子結點為空,則在相應位置上給出“-”。給出的數據間用一個空格分隔。注意:題目保證每個結點中存儲的字母是不同的。
先從大的框架主函數寫起,先不管怎么實現先大膽用各種函數
方法:先建立兩棵樹並返回根結點,再判斷返回結果,根據結果輸出yes或no

int main() { int root1,root2; root1=input(t1); //接收數據生成一棵樹並返回根結點的標號 root2=input(t2); if(match(root1,root2)) //調用函數返回判斷結果 cout<<"Yes"; else cout<<"No"; return 0; }
先把結點的定義確定下來
方法:字符型的數據,整型的左右孩子

typedef struct { char data; int lchild,rchild; }node; //根據題意定義一個結構體放0-n號結點的信息(字母及左右孩子的標號)
建立樹
方法:這個就是老套路了,按照編號(下標)輸入數據和孩子編號,更改孩子結點的flag值,最后遍歷所有結點來找出根節點

int input(node t[]) { //輸入數據並返回根結點的標號 int n; char ch1,ch2; cin>>n; //輸入一棵樹總共結點數 if(n==0) return -1; //如果是空樹就直接返回 不同構了 bool test[n]={false}; //定義一個布爾數組來標記每個結點是否有雙親 for(int i=0;i<n;i++) { //從0開始讀入對應標號的結點的信息 cin>>t[i].data; cin>>ch1>>ch2; if(ch1!='-') { //如果左孩子不為空就成為對應結點的左孩子 t[i].lchild=ch1-'0'; test[ t[i].lchild ]=true; //左孩子標號的那個結點就是有雙親的了 } else t[i].lchild=-1; //左孩子為空則標記為-1 if(ch2!='-') { t[i].rchild=ch2-'0'; test[ t[i].rchild ]=true; } else t[i].rchild=-1; } for(int j=0;j<n;j++) { //從0開始逐一判斷每個結點是否有雙親 if(!test[j]) //沒有雙親的就是根結點 return j; } }
比較判斷是否同構
方法:分兩種情況來討論
樹空的情況1.兩棵樹都為空 (同構) 2.一棵樹為空另一棵樹不為空 (不同構)
樹不為空的情況1.結點的數據不相等(不同構) 2.結點數據相等繼續比較左左孩子和右右孩子(若都同構則兩棵樹同構) 3.結點數據相等但左左孩子和右右孩子不同構則繼續比較左右和右左孩子(若都同構則兩棵樹同構) 4.結點數據相等但左左右右,左右右左都不同構則兩棵樹不同構

int match(int r1,int r2) { //判斷是否同構 if(r1==-1&&r2==-1) return 0;//如果兩棵樹都為空則不同構 if((r1==-1&&r2!=-1) || (r1!=-1&&r2==-1)) return 0; //兩棵樹一棵為空,一棵不為空則不同構 if(t1[r1].data!=t2[r2].data) return 0; //如果非空但數據不相等則不同構 if(match(t1[r1].lchild,t2[r2].lchild) && match(t1[r1].rchild,t2[r2].rchild)) return 1; //如果非空且數據相等則繼續比較左左 右右 if(match(t1[r1].lchild,t2[r2].rchild) && match(t1[r1].rchild,t2[r2].lchild)) return 1; //如果非空且數據相等但左左右右不相等則比較左右 右左 return 0; //以上包含了空的情況和非空時同構的情況最后加上不同構的時候 }
最后貼上全部代碼
#include <iostream> using namespace std; typedef struct { char data; int lchild,rchild; }node; //根據題意定義一個結構體放0-n號結點的信息(字母及左右孩子的標號) node t1[11],t2[11]; //定義兩個結構體類型的數組 int input(node t[]) { //輸入數據並返回根結點的標號 int n; char ch1,ch2; cin>>n; //輸入一棵樹總共結點數 if(n==0) return -1; //如果是空樹就直接返回 不同構了 bool test[n]={false}; //定義一個布爾數組來標記每個結點是否有雙親 for(int i=0;i<n;i++) { //從0開始讀入對應標號的結點的信息 cin>>t[i].data; cin>>ch1>>ch2; if(ch1!='-') { //如果左孩子不為空就成為對應結點的左孩子 t[i].lchild=ch1-'0'; test[ t[i].lchild ]=true; //左孩子標號的那個結點就是有雙親的了 } else t[i].lchild=-1; //左孩子為空則標記為-1 if(ch2!='-') { t[i].rchild=ch2-'0'; test[ t[i].rchild ]=true; } else t[i].rchild=-1; } for(int j=0;j<n;j++) { //從0開始逐一判斷每個結點是否有雙親 if(!test[j]) //沒有雙親的就是根結點 return j; } } int match(int r1,int r2) { //判斷是否同構 if(r1==-1&&r2==-1) return 0;//如果兩棵樹都為空則不同構 if((r1==-1&&r2!=-1) || (r1!=-1&&r2==-1)) return 0; //兩棵樹一棵為空,一棵不為空則不同構 if(t1[r1].data!=t2[r2].data) return 0; //如果非空但數據不相等則不同構 if(match(t1[r1].lchild,t2[r2].lchild) && match(t1[r1].rchild,t2[r2].rchild)) return 1; //如果非空且數據相等則繼續比較左左 右右 if(match(t1[r1].lchild,t2[r2].rchild) && match(t1[r1].rchild,t2[r2].lchild)) return 1; //如果非空且數據相等但左左右右不相等則比較左右 右左 return 0; //以上包含了空的情況和非空時同構的情況最后加上不同構的時候 } int main() { int root1,root2; root1=input(t1); //接收數據生成一棵樹並返回根結點的標號 root2=input(t2); if(match(root1,root2)) //調用函數返回判斷結果 cout<<"Yes"; else cout<<"No"; return 0; }