给定一棵二叉搜索树的先序遍历序列,要求你找出任意两结点的最近公共祖先结点(简称 LCA)。
输入格式:
输入的第一行给出两个正整数:待查询的结点对数 M(≤ 1 000)和二叉搜索树中结点个数 N(≤ 10 000)。随后一行给出 N 个不同的整数,为二叉搜索树的先序遍历序列。最后 M 行,每行给出一对整数键值 U 和 V。所有键值都在整型int范围内。
输出格式:
对每一对给定的 U 和 V,如果找到 A 是它们的最近公共祖先结点的键值,则在一行中输出 LCA of U and V is A.。但如果 U 和 V 中的一个结点是另一个结点的祖先,则在一行中输出 X is an ancestor of Y.,其中 X 是那个祖先结点的键值,Y 是另一个键值。如果 二叉搜索树中找不到以 U 或 V 为键值的结点,则输出 ERROR: U is not found. 或者 ERROR: V is not found.,或者 ERROR: U and V are not found.。
思路:二叉搜索树的中序遍历即为将结点值按照非递减排序得到的结果,而根据输入的先序遍历和中序遍历我们就可以生成一个father数组,用以储存每个结点其父节点的序号(相对顺序序号)。
在寻找最近公共先祖节点的时候,利用两个栈,将待寻找的u、v自身以及其之上的所有祖先节点压入栈内,然后依次弹出两个栈顶的元素,直至找到不相同的两组数据,则上一个栈顶的元素即是最近公共先祖结点。
需要注意的点:
0.判断其中一个节点是否为另一个节点祖先结点的条件
1.输入数据并非是连续的从1到N的数,而是N个Int型的整数。因此father中存储的都是将输入数据按非递减排序得到的相对顺序标号。
2.u,v两个值相同返回其父节点即可。
3.输入u,v相同且为root根节点时返回格式是u是v的最近祖先结点。
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
#include <stdio.h>
using namespace std;
int M,N,root;
queue<int> pro; //输入的先序遍历
vector<int> ino; //中序遍历
int father[10006];
int findOrder(int a){
for(int i = 1; i <= N; i++){
if(a == ino[i]) {
return i;
}
}
return 0;
}
//构造父节点
void Build(int left, int right, int f){
if(left > right) return;
int t = pro.front(); //当前节点的值
pro.pop();
if(left == right) father[left] = f;
else{
for(int i = left; i <= right; i++){
if(t == ino[i]){
Build(left, i-1, i);
Build(i+1, right, i);
father[i] = f;
break;
}
}
}
}
void closest_father(int u, int v){
int u1 = findOrder(u);
int v1 = findOrder(v);
if(u1 == 0 && v1 == 0) {cout << "ERROR: "<<u<<" and "<<v<<" are not found." << endl; return;}
else if(u1 == 0) {cout << "ERROR: "<<u<<" is not found." << endl; return;}
else if(v1 == 0) {cout << "ERROR: "<<v<<" is not found." << endl; return;}
//输入值为两个根节点,则最近公共祖先是自己(根节点)
if(u == v && u == root) { cout << v <<" is an ancestor of " << u << "." << endl; return;}
//如果两个数值相同,直接返回其父结点
if(u == v){ cout <<"LCA of "<< u <<" and "<< v <<" is " << ino[father[u1]] << "." << endl; return;}
stack<int> s1,s2;
int t = u1;
//将u、v和其所有祖先节点压入栈中
while(t != 0){
s1.push(t);
t = father[t];
}
t = v1;
while(t != 0){
s2.push(t);
t = father[t];
}
int a,b,f;
int flag = 0;
while(!s1.empty() && !s2.empty()){
a = s1.top(); s1.pop();
b = s2.top(); s2.pop();
if(a == b) f = a; //flag为0则表示是u或v其中有一个为另一个祖先结点
else { flag = 1; break; }
}
if(s1.empty() && !flag){
cout << u <<" is an ancestor of " << v << "." << endl;
return;
}
if(s2.empty() && !flag){
cout << v <<" is an ancestor of " << u << "." << endl;
return;
}
cout <<"LCA of "<< u <<" and "<< v <<" is " << ino[f] << "." << endl;
}
int main()
{
cin >> M >> N;
int tp;
for(int i = 1; i <= N; i++){
cin >> tp;
pro.push(tp);
ino.push_back(tp);
}
father[0]= 0;
ino.push_back(0);
sort(ino.begin(), ino.end()); //二叉检索树排序后即可得到中序遍历顺序
root = pro.front();
Build(1,N,0);
int u,v;
while(M--){
cin >> u >> v;
closest_father(u,v);
}
return 0;
}