图论专题(二十一):并查集的“工程应用”——拔线重连,修复「连通网络」 - 指南
哈喽各位,我是前端小L。
欢迎来到我们的图论专题第二十一篇!在现实世界的网络工程中,资源往往是有限的。如果我们发现公司里的电脑网络分成了好几个互不相通的局域网,而手头的网线又不够用,该怎么办?
我们必须凭借“拆东墙补西墙”的策略:找到那些“多余”的网线(在一个连通分量内部形成环的线),把它们拔下来,用来连接那些断开的局域网。
这道题的核心在于:如何判断网线“够不够”?以及如果够,大家必须动多少次手?
力扣 1319. 连通网络的处理次数
https://leetcode.cn/problems/number-of-operations-to-make-network-connected/

题目分析:
输入:
n台电脑(节点),connections网线列表(边)。操作:拔掉一根已有的网线,把它连到另外两台电脑之间。
目标:让所有电脑都连通。求最少操作次数。
特殊情况:如果线缆总数太少,无论怎么移都连不通,返回
-1。
核心洞察:“硬性门槛”与“连通数学”
第一道门槛:资源够不够? 图论告诉我们:要连通 n 个节点,至少需要 n - 1 条边(这就构成了一棵生成树)。
如果
connections.size() < n - 1:线缆总数根本不够,神仙难救,直接返回-1。如果
connections.size() >= n - 1:线缆总数足够!既然总数够,我们一定能通过“拆除冗余、填补空缺”的方式,把图变成连通的。
第二道门槛:需要动几次手? 假设我们用并查集处理完所有的现有连接后,发现网络中还剩下 k 个互不相通的连通分量(孤岛)。 要把这 k 个孤岛连成一个整体,我们需要搭几座桥? 很简单,把 k 个点串起来,需要 k - 1 条线。
结论:只要总线缆数达标,我们只得计算出当前的连通分量个数 count,答案就是 count - 1。我们不需要关心具体拔哪根线,缘于题目只问“次数”。
算法流程:并查集的标准作业
硬性检查:如果
connections.size() < n - 1,返回-1。初始化并查集:
parent数组,大小为n。关键:初始化连通分量计数器
componentCount = n(一开始大家都是孤岛)。
遍历连接:
对于每一条边
[u, v],调用union(u, v)。在
union函数内部:如果u和v原本不属于同一个集合(rootU != rootV),合并它们,并将componentCount减 1。
计算结果:
遍历结束后,
componentCount就是剩下的连通分量个数。需要的操作次数 =
componentCount - 1。
代码完成 (并查集模板应用)
C++
#include
#include
using namespace std;
class Solution {
private:
// --- 并查集内部类 ---
class UnionFind {
private:
vector parent;
int count; // 连通分量个数
public:
UnionFind(int n) {
count = n;
parent.resize(n);
iota(parent.begin(), parent.end(), 0); // 初始化 0, 1, 2...
}
int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]); // 路径压缩
}
return parent[x];
}
void unite(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX != rootY) {
parent[rootX] = rootY;
count--; // 每次成功合并,孤岛少一个
}
}
int getCount() const {
return count;
}
};
public:
int makeConnected(int n, vector>& connections) {
// 1. 硬性门槛检查:线缆总数是否足够
if (connections.size() < n - 1) {
return -1;
}
// 2. 初始化并查集
UnionFind uf(n);
// 3. 建立现有的连接
for (const auto& conn : connections) {
uf.unite(conn[0], conn[1]);
}
// 4. 计算需要的操作数
// 剩下的连通分量个数 - 1,就是连接它们所需的边数
return uf.getCount() - 1;
}
};
深度复杂度分析
V (Vertices):电脑数
n。E (Edges):线缆数
connections.size()。时间复杂度 O(n + E * α(n)):
初始化并查集得 O(n)。
遍历
E条边进行合并,每次操作近似 O(1)(阿克曼反函数)。总时间近似线性。
空间复杂度 O(n):
并查集的
parent数组。
总结:冗余与连通的辩证关系
今天这道题,让我们看到了图论中**“数量关系”**的美妙。
冗余边:在一个已经连通的分量内部再加边,就是冗余(这就形成了环)。
连通代价:要把
k个分量连通,必须且只需k-1条边。
只要我们手中的总边数(资源)大于等于总节点数-1(需求),我们就拥有了足够的“冗余边”去填补所有的“连通代价”。并查集在这里,不仅帮我们维护了连通性,通过维护 count 变量,它还直接告诉了我们当前的“分裂程度”。
下一篇,我们将利用并查集处理一种更有趣的逻辑关系——“等式方程的可满足性”。当 == 和 != 混杂在一起时,我们该如何判断逻辑是否冲突?
下期见!
浙公网安备 33010602011771号