目錄
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(* ̄▽ ̄*)ブ
方法二