并查集(Union-Find)主要用于处理动态连通性问题,常见套路包括以下几个方面:
1. 基本操作
- 初始化
vector<int> parent(n); iota(parent.begin(), parent.end(), 0); // 每个节点的父节点初始化为自己 - 查找(Find)
int find(int x) { if (parent[x] != x) parent[x] = find(parent[x]); // 路径压缩 return parent[x]; } - 合并(Union)
void unite(int x, int y) { parent[find(x)] = find(y); }
2. 优化策略
- 路径压缩(Path Compression):在
find过程中,将树的深度压缩,加速后续查询。 - 按秩合并(Union by Rank):合并时,总是将低秩树挂在高秩树上,减少树的高度。
vector<int> rank(n, 1); // 初始秩为1 void unite(int x, int y) { int rootX = find(x), rootY = find(y); if (rootX != rootY) { if (rank[rootX] > rank[rootY]) parent[rootY] = rootX; else if (rank[rootX] < rank[rootY]) parent[rootX] = rootY; else { parent[rootY] = rootX; rank[rootX]++; } } }
3. 常见应用套路
(1) 连接问题
题型:判断两点是否连通、计算连通分量数量。
例题:LeetCode 547. 省份数量
int findCircleNum(vector<vector<int>>& isConnected) {
int n = isConnected.size();
vector<int> parent(n);
iota(parent.begin(), parent.end(), 0);
function<int(int)> find = [&](int x) {
return parent[x] == x ? x : (parent[x] = find(parent[x]));
};
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++)
if (isConnected[i][j])
parent[find(i)] = find(j);
unordered_set<int> provinces;
for (int i = 0; i < n; i++)
provinces.insert(find(i));
return provinces.size();
}
(2) 冗余连接
题型:给定一张无向图,判断它是否存在环。
例题:LeetCode 684. 冗余连接
vector<int> findRedundantConnection(vector<vector<int>>& edges) {
int n = edges.size();
vector<int> parent(n + 1);
iota(parent.begin(), parent.end(), 0);
function<int(int)> find = [&](int x) {
return parent[x] == x ? x : (parent[x] = find(parent[x]));
};
for (auto& edge : edges) {
int u = edge[0], v = edge[1];
if (find(u) == find(v)) return edge; // 发现环
parent[find(u)] = find(v);
}
return {};
}
(3) 最小生成树(Kruskal 算法)
题型:最小代价连接所有点。
例题:LeetCode 1584. 连接所有点的最小费用
int minCostConnectPoints(vector<vector<int>>& points) {
int n = points.size();
vector<tuple<int, int, int>> edges;
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++)
edges.push_back({abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1]), i, j});
sort(edges.begin(), edges.end());
vector<int> parent(n);
iota(parent.begin(), parent.end(), 0);
function<int(int)> find = [&](int x) {
return parent[x] == x ? x : (parent[x] = find(parent[x]));
};
int cost = 0, count = 0;
for (auto& [w, u, v] : edges) {
if (find(u) != find(v)) {
parent[find(u)] = find(v);
cost += w;
if (++count == n - 1) break;
}
}
return cost;
}
4. 总结套路
- 查找是否属于同一集合 →
find(x) == find(y) - 合并集合 →
unite(x, y) - 路径压缩 & 按秩合并优化查询效率
- 应用方向:
- 计算连通分量
- 检测环
- 最小生成树(Kruskal 算法)
- 解决等式/不等式约束问题
这样整理后,你可以在不同题型中直接套用核心模板,提高解题效率 🚀
547

被折叠的 条评论
为什么被折叠?



