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(* ̄▽ ̄*)ブ

方法二

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值