思路一
根据先序序列依次将结点插入树中,之后对于给出的每一对结点,分别找到两个结点从根到该结点的路径p1
和p2
。两个测试点运行超时。
#include<cstdio>
#include<vector>
using namespace std;
int queryNum, keyNum;
struct node {
int val;
node *left, *right;
node(int v) {
val = v;
left = NULL;
right = NULL;
}
}*root;
void insert(node *&p, int val) {
if (p == NULL) {
p = new node(val);
return;
}
if (val < p->val)
insert(p->left, val);
else insert(p->right, val);
}
int main() {
scanf("%d %d", &queryNum, &keyNum);
for (int i = 0;i < keyNum;i++) {
int temp;
scanf("%d", &temp);
insert(root, temp);
}
for (int i = 0;i < queryNum;i++) {
int a, b;
scanf("%d %d", &a, &b);
vector<int> p1, p2;
node *temp = root;
while (temp != NULL) {
p1.push_back(temp->val);
if (temp->val == a)
break;
if (a < temp->val) {
temp = temp->left;
}
else temp = temp->right;
}
temp = root;
while (temp != NULL) {
p2.push_back(temp->val);
if (temp->val == b)
break;
if (b < temp->val) {
temp = temp->left;
}
else temp = temp->right;
}
if (p1[p1.size() - 1] != a && p2[p2.size() - 1] != b) {
printf("ERROR: %d and %d are not found.\n", a, b);
}
else if (p1[p1.size() - 1] != a) {
printf("ERROR: %d is not found.\n", a);
}
else if (p2[p2.size() - 1] != b) {
printf("ERROR: %d is not found.\n", b);
}
else {
int index = 0;
while (index < p1.size() && index < p2.size() && p1[index] == p2[index])
index++;
index--;
if (p1[index] == a) {
printf("%d is an ancestor of %d.\n", a, b);
}
else if (p1[index] == b) {
printf("%d is an ancestor of %d.\n", b, a);
}
else {
printf("LCA of %d and %d is %d.\n", a, b, p1[index]);
}
}
}
}
思路二
考虑到结点数较多,不用每次插入一个结点的方式,而选择根据先序遍历的特点(先访问根结点,再访问左子树,最后访问右子树)和BST的特点,借助栈进行建树,全部测试点通过:
#include<cstdio>
#include<vector>
#include<stack>
#include<cmath>
using namespace std;
vector<int> preorder;
int queryNum, keyNum;
struct node {
int val;
node *left, *right, *parent;
node(int v) {
val = v;
left = NULL;
right = NULL;
parent = NULL;
}
}*root;
void insert(node *&p, int val) {
if (p == NULL) {
p = new node(val);
return;
}
if (val < p->val)
insert(p->left, val);
else insert(p->right, val);
}
//检查插入该位置是否违反BST的性质
bool isInsertPos(node *pos, int val) {
while (pos->parent != NULL) {
if (pos->parent->left == pos) { //该结点是左子树
if (abs(pos->parent->val) < abs(val))
return false;
}
else {
if (abs(pos->parent->val) > abs(val))
return false;
}
pos = pos->parent;
}
return true;
}
int main() {
scanf("%d %d", &queryNum, &keyNum);
preorder.resize(keyNum);
for (int i = 0;i < keyNum;i++) {
scanf("%d", &preorder[i]);
}
//建树
int index = 0;
stack<node*> st;
node *root = new node(preorder[index++]), *cur = root;
st.push(root);
while (index < preorder.size()) {
while (index < preorder.size() && abs(preorder[index]) < abs(cur->val)) {
cur->left = new node(preorder[index++]);
cur->left->parent = cur;
cur = cur->left;
st.push(cur);
}
if (index < preorder.size()) {
while (!isInsertPos(cur, preorder[index])) {
cur = st.top();
st.pop();
}
cur->right = new node(preorder[index++]);
cur->right->parent = cur;
cur = cur->right;
st.push(cur);
}
}
for (int i = 0;i < queryNum;i++) {
int a, b;
scanf("%d %d", &a, &b);
vector<int> p1, p2;
node *temp = root;
while (temp != NULL) {
p1.push_back(temp->val);
if (temp->val == a)
break;
if (a < temp->val) {
temp = temp->left;
}
else temp = temp->right;
}
temp = root;
while (temp != NULL) {
p2.push_back(temp->val);
if (temp->val == b)
break;
if (b < temp->val) {
temp = temp->left;
}
else temp = temp->right;
}
if (p1[p1.size() - 1] != a && p2[p2.size() - 1] != b) {
printf("ERROR: %d and %d are not found.\n", a, b);
}
else if (p1[p1.size() - 1] != a) {
printf("ERROR: %d is not found.\n", a);
}
else if (p2[p2.size() - 1] != b) {
printf("ERROR: %d is not found.\n", b);
}
else {
int index = 0;
while (index < p1.size() && index < p2.size() && p1[index] == p2[index])
index++;
index--;
if (p1[index] == a) {
printf("%d is an ancestor of %d.\n", a, b);
}
else if (p1[index] == b) {
printf("%d is an ancestor of %d.\n", b, a);
}
else {
printf("LCA of %d and %d is %d.\n", a, b, p1[index]);
}
}
}
}
思路三
对先序序列排序得到中序序列,根据这两个序列可以把树建出来。
#include<iostream>
#include<string>
#include<unordered_map>
#include<unordered_set>
#include<vector>
#include<cctype>
#include<algorithm>
using namespace std;
vector<int> preorder, inorder;
unordered_map<int, int> inIndex;
struct node {
int val;
node *left, *right;
node(int v) {
val = v;
left = NULL;
right = NULL;
}
}*root = NULL;
void build(node *&cur, int preLeft, int preRight, int inLeft, int inRight) {
if (inLeft > inRight) {
return;
}
cur = new node(preorder[preLeft]);
int inMid = inIndex[preorder[preLeft]], leftCount = inMid - inLeft, rightCount = inRight - inMid;
build(cur->left, preLeft + 1, preLeft + leftCount, inLeft, inMid - 1);
build(cur->right, preRight - rightCount + 1, preRight, inMid + 1, inRight);
}
void getPath(node *cur, int val, vector<int> &path) {
if (cur == NULL) {
return;
}
path.push_back(cur->val);
if (cur->val > val) {
getPath(cur->left, val, path);
}
else if(cur->val < val){
getPath(cur->right, val, path);
}
else return;
}
int main() {
int numTest, numKey;
cin >> numTest >> numKey;
preorder.resize(numKey);
for (int i = 0; i < numKey; i++) {
scanf("%d", &preorder[i]);
}
inorder = preorder;
sort(inorder.begin(), inorder.end());
for (int i = 0; i < numKey; i++) {
inIndex[inorder[i]] = i;
}
build(root, 0, numKey - 1, 0, numKey - 1);
for (int i = 0; i < numTest; i++) {
vector<int> p1, p2;
int u, v;
scanf("%d %d", &u, &v);
getPath(root, u, p1);
getPath(root, v, p2);
int index = 0;
if (p1.back() != u || p2.back() != v) {
if (p1.back() != u && p2.back() != v) {
printf("ERROR: %d and %d are not found.\n", u, v);
}
else if (p1.back() != u) {
printf("ERROR: %d is not found.\n", u);
}
else {
printf("ERROR: %d is not found.\n", v);
}
continue;
}
while (index < p1.size() && index < p2.size() && p1[index] == p2[index]) {
++index;
}
if (index >= p1.size() || index >= p2.size()) {
if (index >= p2.size()) {
swap(u, v);
}
printf("%d is an ancestor of %d.\n", u, v);
}
else {
printf("LCA of %d and %d is %d.\n", u, v, p1[index - 1]);
}
}
}
思路四
根据BST的特点,它的中序序列是有序的,而中序序列的访问顺序是:先左子树,再访问根结点,最后访问右子树。
对于中序序列的一个位置,在它左边的都是左子结点,在它右边的都是右子结点。如果一个结点是a, b的祖先但不是最低公共祖先,则a, b都在这个结点的左子树,或者a, b都在这个结点的右子树。换言之,LCA一定是这样一个结点:u
和v
分别在它的左子树和右子树,否则u
和v
其中之一是父结点。
在中序序列中,当lca
被“夹”在u
和v
之间时,则说明preorder[i]
已经是最低的公共祖先了,因为这时u
和v
分别在preorder[i]
对应结点的左右子树中。
特殊情况是u
和v
其中之一就是那个根节点,这时u == preorder[i]
或者v == preorder[i]
。
从先序序列开始,逐个遍历,直到符合上述条件为止。(为什么要从先序序列开始?因为夹在中序序列两个数之间的,除了两结点的最低公共祖先,还有可能是其中一个结点的兄弟结点。为了找到LCA然后及时退出,根据先序序列先访问根,再访问左右子结点的特点,所以从先序序列开始)实现代码如下:
#include <iostream>
#include <vector>
#include <unordered_set>
#include<algorithm>
using namespace std;
unordered_set<int> exist; //标记出现的结点
int main() {
int m, n, u, v, a;
scanf("%d %d", &m, &n);
vector<int> preorder(n);
for (int i = 0; i < n; i++) {
scanf("%d", &preorder[i]);
exist.insert(preorder[i]);
}
for (int i = 0; i < m; i++) {
scanf("%d %d", &u, &v);
for (int j = 0; j < n; j++) {
a = preorder[j];
if(a >= min(u, v) && a <= max(u, v)) break; //preorder[j]被夹在u和v之间
}
if (exist.count(u) == 0 && exist.count(v) == 0)
printf("ERROR: %d and %d are not found.\n", u, v);
else if (exist.count(u) == 0 || exist.count(v) == 0)
printf("ERROR: %d is not found.\n", exist.count(u) == 0 ? u : v);
else if (a == u || a == v)
printf("%d is an ancestor of %d.\n", a, a == u ? v : u);
else
printf("LCA of %d and %d is %d.\n", u, v, a);
}
}