📌  相关文章
📜  使用 LCA 查询以查找给定树中两个节点之间的最大和最小权重。(1)

📅  最后修改于: 2023-12-03 15:06:48.350000             🧑  作者: Mango

使用 LCA 查询以查找给定树中两个节点之间的最大和最小权重

LCA (Lowest Common Ancestor) 问题在树的查询中经常被遇到。其中一种变形是通过 LCA 查询两个节点之间的最大和最小权重。

定义

树是由节点和各节点之间的边组成的一种数据结构。每个节点包含一个权重(可以为空),每个边连接两个节点。

LCA 是指在树中,两个节点最近的共同祖先节点。

问题

给定一棵有根树和两个节点,需要在这个树中查询它们之间的最大和最小权重。

解决方案

为了解决此问题,我们可以使用 LCA。首先,我们需要对树进行预处理以构建一个父亲数组和一个深度数组。

父亲数组

父亲数组是一个包含每个节点的父节点的数组。它的构建可以通过深度优先遍历(DFS)树来实现。

int parent[N]; // N 表示节点数量
vector<int> adj[N]; // 存储节点的边

void dfs(int node, int father) {
    parent[node] = father;
    for (int i = 0; i < adj[node].size(); i++) {
        int child = adj[node][i];
        if (child != father) {
            dfs(child, node);
        }
    }
}
深度数组

深度数组包含每个节点的深度。它可以通过深度优先遍历树来实现。在遍历时,我们可以通过记录每个节点的深度来计算其子节点的深度。

int depth[N]; // N 表示节点数量

void dfs(int node, int father, int d) {
    depth[node] = d;
    for (int i = 0; i < adj[node].size(); i++) {
        int child = adj[node][i];
        if (child != father) {
            dfs(child, node, d + 1);
        }
    }
}

有了上述预处理,接下来我们就可以计算 LCA,并通过 LCA 计算最大和最小权重了。

计算 LCA

按照 LCA 的定义,我们可以通过向上跳两个节点的深度差来找到它们的 LCA。如果一个节点的深度大于另一个节点,那么我们就向上跳它们的深度差,直到找到一个节点的深度等于另一个节点的深度。然后,我们就可以继续向上跳,直到找到它们共同的祖先。

int lca(int x, int y) {
    while (depth[x] > depth[y]) x = parent[x];
    while (depth[x] < depth[y]) y = parent[y];
    while (x != y) {
        x = parent[x];
        y = parent[y];
    }
    return x;
}
计算最大和最小权重

有了 LCA,我们就可以轻松地计算最大和最小权重。对于一个节点的权重,我们可以在和它的 LCA 之间找到所有节点的最大和最小权重。为了实现这一点,我们可以在向上跳查找 LCA 时记录最大值和最小值。

int mx = 0, mn = INF;
void update(int node, int lca) {
    while (depth[node] > depth[lca]) {
        mx = max(mx, weight[node]);
        mn = min(mn, weight[node]);
        node = parent[node];
    }
}

void calc(int x, int y) {
    int ancestor = lca(x, y);
    mx = 0, mn = INF;
    update(x, ancestor);
    update(y, ancestor);
    printf("Max weight: %d\n", mx);
    printf("Min weight: %d\n", mn);
}
总结

通过预处理父亲数组和深度数组,我们可以计算 LCA,并利用 LCA 计算任意两个节点的最大和最小权重。