C++c4.cpp — 判祖先与求路径
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 双亲数组 + 深度,判断祖先关系并输出两点间的路径
const int MAXN = 105;
int fa[MAXN], dep[MAXN];
int n;
bool isAnc(int u, int v) { // u 是否为 v 的祖先(或就是 v 本身)
while (v != 0) { // 根的双亲是 0,一直往上跳
if (v == u) return true;
v = fa[v];
}
return false;
}
int main() {
int root = 0;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> fa[i];
if (fa[i] == 0) root = i;
}
for (int i = 1; i <= n; i++) { // 逐个往上数,得到每个结点的深度
int x = i, d = 1;
while (fa[x] != 0) { d++; x = fa[x]; }
dep[i] = d;
}
cout << "根:" << root << endl;
int q; cin >> q;
while (q--) {
int u, v; cin >> u >> v;
cout << u << " 是 " << v << " 的祖先?" << (isAnc(u, v) ? "Yes" : "No") << endl;
}
int u, v; cin >> u >> v; // 最后再给一对,输出它们之间的路径
int a = u, b = v;
while (dep[a] > dep[b]) a = fa[a]; // 深的先往上跳,拉到同一层
while (dep[b] > dep[a]) b = fa[b];
while (a != b) { a = fa[a]; b = fa[b]; } // 再同步上跳,相遇处即 LCA
int lca = a;
vector<int> path;
int x = v;
while (x != lca) { path.push_back(x); x = fa[x]; }
path.push_back(lca);
reverse(path.begin(), path.end());
cout << "LCA(" << u << "," << v << ") = " << lca << endl;
cout << u << " 到 " << v << " 的路径:";
for (int i = 0; i < (int)path.size(); i++) cout << path[i] << " ";
cout << endl;
return 0;
}
输入
8
0 1 1 1 2 2 4 6
3
2 8
4 8
1 6
2 8
运行结果
根:1
2 是 8 的祖先?Yes
4 是 8 的祖先?No
1 是 6 的祖先?Yes
LCA(2,8) = 2
2 到 8 的路径:2 6 8
核心技巧:求 LCA 时,先让深度大的那个往上跳,直到两者深度相同;再同步上跳,相遇处即为最近公共祖先。这是最朴素的 LCA 写法,时间 O(深度)。
为什么必须两个数组:dep[] 用于比较深度,fa[] 用于往上跳 —— 这正是模块二"实战组合"那一栏说的:建树时顺手记下 fa,向上向下都畅通。
说明:LCA 的高效解法(倍增、Tarjan)属于提高级内容,此处只要求掌握朴素写法。
思路:用双亲数组配合深度数组:先把 u、v 各自向上爬到同一深度,再同步向上走,第一次相遇的结点就是 LCA;沿途把经过的结点记录下来即为两点间的路径。
易错:忘了先把较深的那个结点“对齐”到相同深度,导致两个指针永远错位、相遇点判断失败。