PAT_甲級_1143 Lowest Common Ancestor (30point(s)) (C++)【BST構建/尋找LCA/倍增法】

目錄

1,題目描述

題目大意

輸入

2,思路

方法一:

數據結構

算法

方法二(倍增法):

數據結構

算法

3,AC代碼

方法一: 

方法二(倍增法):

4,解題過程

方法一 

方法二


1,題目描述

Sample Input:

6 8
6 3 1 2 5 4 8 7
2 5
8 7
1 9
12 -3
0 8
99 99

 

Sample Output:

LCA of 2 and 5 is 3.
8 is an ancestor of 7.
ERROR: 9 is not found.
ERROR: 12 and -3 are not found.
ERROR: 0 is not found.
ERROR: 99 and 99 are not found.

題目大意

給出一棵BST的先序遍歷(做過前面題目的同學就知道了,這裏暗含一箇中序遍歷序列,因而可以構造一棵BST),接着給出若干對節點,判斷兩個節點是否存在於BST中,以及這兩個節點的The lowest common ancestor (LCA)是誰。

輸入

  1. 第一行:查詢數目M,頂點數目N;
  2. 第二行:先序遍歷序列;
  3. 其餘M行:每行包含兩個節點;

 

2,思路

方法一:

數據結構

  • struct node{
        int key;
        node *left = NULL, *right = NULL;
    };構建二叉樹的常用結構體形式;
  • int pre[10005], in[10005]:分別存放先序遍歷和中序遍歷,用於構建BST;

算法

  1. 根據先序遍歷獲得中序遍歷:
  2. 先序遍歷+中序遍歷=》構建樹:(算法很常見了,建議牢記)
  3. 設計judge函數,設計兩大部分。第一部分,尋找兩個節點在BST中的路徑,存入vector中,若節點不存在,則vector爲空:
  4. 第二部分,根據路徑信息,判斷輸出:

方法二(倍增法):

參考大佬的解題思路@日沉雲起【pat甲級1143. Lowest Common Ancestor (30)、甲級1151. LCA in a Binary Tree(30)】

方法二與方法一相比的巧妙之處:

1,不需要利用指針(動態構建,利用指針容易出錯),而是利用靜態數組+結構體的方法獲取整棵樹的描述;

2,結構體的設計,由於不需要完整的遍歷樹中的每一個節點,而只需從一個節點不斷尋找父節點,向根節點迭代,故只需要一個father指向當前節點的父節點的位置;

3,藉助於pre序列的數組形式,將普通數組轉化爲結構體數組,從而記錄每個節點通往根節點的路徑;

數據結構

  • struct node{
        int key, father, level;
    }pre[10005];key:當前節點的值,father當前節點的父節點在pre數組中的位置,level當前節點在樹種的層次;
  • int in[10005]:記錄中序遍歷;

算法

  1. 構建BST(其實只是記錄了每個節點的父節點位置):
  2. judge函數分爲兩部分。第一部分:尋找a,b在BST中的位置:
  3. 第二部分,將較深的節點提升到與另一節點相同的高度,並逐步向上迭代,尋找相同的祖先節點:

 

3,AC代碼

方法一: 

雖然代碼很臃腫,但是個人覺得邏輯還是比較簡單、不容易出錯的。。。(✿◡‿◡) 

#include<bits/stdc++.h>
using namespace std;
struct node{
    int key;
    node *left = NULL, *right = NULL;
};
int pre[10005], in[10005], M, N;

void buildBST(int root, int left, int right, node *&n){
    if(left > right) return;
    n = new node();
    n->key = pre[root];
    int i = left;
    while(i <= right && in[i] != pre[root])
        i++;
    buildBST(root+1, left, i - 1, n->left);
    buildBST(root+(i-left)+1, i+1, right, n->right);
}
void judge(int a, int b, node *BST){
    vector<int> A, B;
    node *n = BST;
    while(n != NULL && n->key != a){            //尋找a在BST中的路徑
        A.push_back(n->key);
        if(a >= n->key) n = n->right;
        else n = n->left;
    }
    if(n == NULL) A.clear();                    // a不在BST中
    else A.push_back(a);                        // !!!a存在於BST中
    n = BST;
    while(n != NULL && n->key != b){            //尋找b在BST中的路徑
        B.push_back(n->key);
        if(b >= n->key) n = n->right;
        else n = n->left;
    }
    if(n == NULL) B.clear();                    // b不在BST中
    else B.push_back(b);                        // !!!b存在於BST中
    if(A.size() == 0 && B.size() == 0)          //a b均不在BST中
        printf("ERROR: %d and %d are not found.\n", a, b);
    else if(A.size() == 0 && B.size() != 0)     
        printf("ERROR: %d is not found.\n", a);
    else if(A.size() != 0 && B.size() == 0)
        printf("ERROR: %d is not found.\n", b);
    else{                                       //a b均在BST中
        int i = 0;                              //i 定位最深的一個相同根節點
        while(i < A.size() && i < B.size() && A[i] == B[i])
            i++;
        if(A[i-1] == a)                         //由於循環設計的原因 i需要減一
            printf("%d is an ancestor of %d.\n", a, b);
        else if(A[i-1] == b)
            printf("%d is an ancestor of %d.\n", b, a);
        else
            printf("LCA of %d and %d is %d.\n", a, b, A[i-1]);
    }
}
int main(){
#ifdef ONLINE_JUDGE
#else
    freopen("1.txt", "r", stdin);
#endif // ONLINE_JUDGE
    cin>>M>>N;
    for(int i = 0; i < N; i++){
        scanf("%d", &pre[i]);
    }
    memcpy(in, pre, N * sizeof(int));
    sort(in, in + N);
    node *BST = NULL;
    buildBST(0, 0, N-1, BST);
    int a, b;
    for(int i = 0; i < M; i++){
        scanf("%d%d", &a, &b);
        judge(a, b, BST);
    }
    return 0;
}

 

方法二(倍增法):

#include<bits/stdc++.h>
using namespace std;
struct node{
    int key, father, level;
}pre[10005];
int in[10005], M, N;

void createBST(int root, int left, int right, int father, int level){//father當前節點的父節點在先序遍歷結構體數組中的位置
    if(left > right) return;
    int i = left;
    while(i <= right && in[i] != pre[root].key)
        i++;
    pre[root] = {pre[root].key, father, level};//記錄當前節點的值、父節點在pre中的位置、層次
    createBST(root+1, left, i - 1, root, level+1);// !!!是root不是pre[root].key 即父節點在先序遍歷序列中的位置
    createBST(root+(i-left)+1, i+1, right, root, level+1);
}
void judge(int a, int b){
    int aF = N, bF = N;// aF/bF即a/b在pre中的位置 初始爲N
    for(int i = 0; i < N; i++){
        if(pre[i].key == a)
            aF = i;
        if(pre[i].key == b)
            bF = i;
    }
    if(aF == N && bF == N)//a b均不包含在BST中
        printf("ERROR: %d and %d are not found.\n", a, b);
    else if(aF == N)
        printf("ERROR: %d is not found.\n", a);
    else if(bF == N)
        printf("ERROR: %d is not found.\n", b);
    else{
        bool flag = true;//a深度大於b
        if(pre[aF].level < pre[bF].level){
            swap(aF, bF);
            flag = false;//a深度小於b
        }
        while(pre[aF].level > pre[bF].level)
            aF = pre[aF].father;
        if(pre[aF].key == pre[bF].key){
            printf("%d is an ancestor of %d.\n", !flag ? a : b, !flag ? b : a);// 注意flag取值
        }else{
            while(pre[aF].key != pre[bF].key){// a b必有相同的祖先節點
                aF = pre[aF].father;
                bF = pre[bF].father;
            }
            printf("LCA of %d and %d is %d.\n", a, b, pre[aF].key);
        }
    }
}
int main(){
#ifdef ONLINE_JUDGE
#else
    freopen("1.txt", "r", stdin);
#endif // ONLINE_JUDGE
    cin>>M>>N;
    for(int i = 0; i < N; i++){
        scanf("%d", &pre[i].key);
        in[i] = pre[i].key;
    }
    sort(in, in + N);
    createBST(0, 0, N-1, -1, 1);
    int a, b;
    for(int i = 0; i < M; i++){
        scanf("%d%d", &a, &b);
        judge(a, b);
    }
    return 0;
}

 

 

4,解題過程

方法一 

一發入魂o(* ̄▽ ̄*)ブ

方法二

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章