目录
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)是谁。
输入
- 第一行:查询数目M,顶点数目N;
- 第二行:先序遍历序列;
- 其余M行:每行包含两个节点;
2,思路
方法一:
数据结构
- struct node{
int key;
node *left = NULL, *right = NULL;
};构建二叉树的常用结构体形式; - int pre[10005], in[10005]:分别存放先序遍历和中序遍历,用于构建BST;
算法
- 根据先序遍历获得中序遍历:
- 先序遍历+中序遍历=》构建树:(算法很常见了,建议牢记)
- 设计judge函数,设计两大部分。第一部分,寻找两个节点在BST中的路径,存入vector中,若节点不存在,则vector为空:
- 第二部分,根据路径信息,判断输出:
方法二(倍增法):
参考大佬的解题思路@日沉云起【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]:记录中序遍历;
算法
- 构建BST(其实只是记录了每个节点的父节点位置):
- judge函数分为两部分。第一部分:寻找a,b在BST中的位置:
- 第二部分,将较深的节点提升到与另一节点相同的高度,并逐步向上迭代,寻找相同的祖先节点:
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(* ̄▽ ̄*)ブ
方法二