PTA資料結構 5-3 樹的同構
題目:
給定兩棵樹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
輸入樣例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
分析與思路:
此題是給出了兩個樹,來判斷這兩棵樹是否屬於同構,而何為同構呢?根據題中的意思,我們可以知道,兩棵樹中包含的結點個數和元素必須相同的,而對於第一棵樹的每一個結點呢,在第一棵樹我們都能找到一個與之對應的結點,並且它們的左右孩子結點的元素是相同的話,當然左右可以互換,這樣的兩棵樹就是同構的。
通過上面的解釋我們就可以有這樣一個思路,首先肯定是通過題目的輸入來構造出兩棵樹,然後我們就對第一棵樹中每一個結點來找出第二棵樹中的對應結點,然後判斷它們的孩子結點元素是否是一樣的,這就是按照題目的意思來實現。這就是方法一。
而我呢自己做題時卻產生了一種另外的想法,既然每個結點需要判斷它們的孩子結點的元素是否一樣,我們需要分不少的情況來考慮,還有空的情況,所以很繁瑣。而我們換一種思路,我們以它們的孩子結點為基準,來判斷它們的父親結點,是不是更好呢?這樣我們就完全不用考慮左右孩子的那麼多種情況了,只需要想如果兩個結點他們的元素相同,那麼它們的父親結點是否是一樣的元素,如果不是,那麼這兩棵樹必然不符合同構,這就是我自己想的方法二。
下面給出兩種方法的程式碼,都通過了測試。第一種是採用了mooc裡的程式碼,沒怎麼改,第二種自己實現了。
method_1 code:
#include <iostream>
using namespace std;
#define MaxTree 10
typedef char ElementType;
typedef int Tree;
struct TreeNode
{
ElementType Data;
Tree Left;
Tree Right;
} T1[MaxTree], T2[MaxTree];
Tree BuildTree(struct TreeNode T[]);
bool Isomorphic(Tree R1, Tree R2);
int main()
{
Tree R1, R2;
R1 = BuildTree(T1);
R2 = BuildTree(T2);
if (Isomorphic(R1, R2))
printf("Yes\n");
else
printf("No\n");
return 0;
}
Tree BuildTree(struct TreeNode T[])
{
int N;
Tree Root; // 根結點
cin>>N;
if (N) {
int *check = new int[N];
for (int i = 0; i < N; i++)
check[i] = 0;
for (int i = 0; i < N; i++) {
char c_left, c_right;
cin >> T[i].Data >> c_left >> c_right;
if (c_left != '-') {
T[i].Left = c_left - '0';
check[T[i].Left] = 1;
}
else {
T[i].Left = -1;
}
if (c_right != '-') {
T[i].Right = c_right - '0';
check[T[i].Right] = 1;
}
else {
T[i].Right = -1;
}
}
int i;
for (i = 0; i < N; i++) {
if (!check[i])
break;
}
Root = i;
}
else
Root = -1;
return Root;
}
bool Isomorphic(Tree R1, Tree R2)
{
/* both empty */
if ((R1 == -1) && (R2 == -1))
return true;
/* one of them is empty */
if (((R1 == -1) && (R2 != -1)) || ((R1 != -1) && (R2 == -1)))
return false;
/* roots are different */
if (T1[R1].Data != T2[R2].Data)
return false;
/* both have no left subtree */
if ((T1[R1].Left == -1) && (T2[R2].Left == -1))
return Isomorphic(T1[R1].Right, T2[R2].Right);
/* no need to swap the left and the right */
if (((T1[R1].Left != -1) && (T2[R2].Left != -1))
&& ((T1[T1[R1].Left].Data) == (T2[T2[R2].Left].Data)))
return (Isomorphic(T1[R1].Left, T2[R2].Left) && Isomorphic(T1[R1].Right, T2[R2].Right));
/* need to swap the left and the right */
else
return (Isomorphic(T1[R1].Left, T2[R2].Right) && Isomorphic(T1[R1].Right, T2[R2].Left));
}
method_2 code:
#include <iostream>
#include <vector>
using namespace std;
typedef char ElementType;
typedef struct TreeNode* BinTree;
struct TreeNode {
ElementType Data;
BinTree Left = NULL;
BinTree Right = NULL;
BinTree Parent = NULL;
};
vector<TreeNode> create_vector(int N);
bool isomorphic(vector<TreeNode> v1, vector<TreeNode> v2);
int main()
{
int N;
cin >> N;
vector<TreeNode> v1 = create_vector(N);
cin >> N;
vector<TreeNode> v2 = create_vector(N);
if (isomorphic(v1, v2))
cout << "Yes" << endl;
else
cout << "No" << endl;
return 0;
}
vector<TreeNode> create_vector(int N)
{
// 建立一個數組來存放各個結點
vector<TreeNode> vec(N);
char data;
char left, right;
// 初始化
for (int i = 0; i < N; i++) {
TreeNode *t_node = new TreeNode;
cin >> data >> left >> right;
t_node->Data = data;
/* 對輸入進行處理並存儲 */
if (left != '-') {
t_node->Left = &vec[(left - '0')];
vec[(left - '0')].Parent = t_node;
}
if (right != '-') {
t_node->Right = &vec[(right - '0')];
vec[(right - '0')].Parent = t_node;
}
vec[i].Data = t_node->Data;
vec[i].Left = t_node->Left;
vec[i].Right = t_node->Right;
}
return vec;
}
/* 對每個vec中的元素分析,通過判斷它們的父節點資料是否一樣來判斷它們是否同構 */
bool isomorphic(vector<TreeNode> v1, vector<TreeNode> v2)
{
/* 如果兩棵樹結點個數不一樣,肯定錯誤 */
if (v1.size() != v2.size())
return false;
bool flag = false;
for (int i = 0; i < v1.size(); i++) {
for (int j = 0; j < v2.size(); j++) {
/* 當找到元素相同的結點時 */
if (v1[i].Data == v2[j].Data) {
flag = true;
/* 兩個節點的父親結點均不為空 */
if (v1[i].Parent && v2[j].Parent) {
/* 父結點的元素也相同時,說明找到了 */
if (v1[i].Parent->Data != v2[j].Parent->Data) {
return false;
}
}
/* 當兩個元素結點中存在沒有沒有父結點的結點時 */
else {
/* 如果都為空,說明是相同的 */
if (v1[i].Parent == NULL && v2[j].Parent == NULL) {
flag = true;
break;
}
/* 其他情況都是表示兩個結點元素相同,但父結點元素不同,必然不是同構 */
else {
return false;
}
}
}
/* 此時還需判斷一下經過一次遍歷,v2中是否找到了與v1結點元素相同結點,若沒有則必然不是同構*/
if (j == v2.size() - 1 && !flag)
return false;
}
}
return true;
}