1. 程式人生 > >PTA資料結構 5-3 樹的同構

PTA資料結構 5-3 樹的同構

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

圖1
圖1
圖2
圖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;
}