算法学习笔记(1) : 并查集 - 知乎 (zhihu.com)
并查集的理论知识可以参考上面这篇知乎,下面我们通过例题来看一下在实际运用中如何利用并查集来解决问题。
给定一个查询时,我们可以遍历 edgeList\textit{edgeList}edgeList 中的所有边,依次将长度小于 limit\textit{limit}limit 的边加入到并查集中,然后使用并查集查询 ppp 和 qqq 是否属于同一个集合。如果 ppp 和 qqq 属于同一个集合,则说明存在从 ppp 到 qqq 的路径,且这条路径上的每一条边的长度都严格小于 limit\textit{limit}limit,查询返回 true\text{true}true,否则查询返回 false\text{false}false。
如果 queries\textit{queries}queries 的 limit\textit{limit}limit 是非递减的,显然上一次查询的并查集里的边都是满足当前查询的 limit\textit{limit}limit 要求的,我们只需要将剩余的长度小于 limit\textit{limit}limit 的边加入并查集中即可。基于此,我们首先将 edgeList\textit{edgeList}edgeList 按边长度从小到大进行排序,然后将 queries\textit{queries}queries 按 limit\textit{limit}limit 从小到大进行排序,使用 kkk 指向上一次查询中不满足 limit\textit{limit}limit 要求的长度最小的边,显然初始时 k=0k=0k=0。
我们依次遍历 queries\textit{queries}queries:如果 kkk 指向的边的长度小于对应查询的 limit\textit{limit}limit,则将该边加入并查集中,然后将 kkk 加 111,直到 kkk 指向的边不满足要求;最后根据并查集查询对应的 ppp 和 qqq 是否属于同一集合来保存查询的结果。
class Solution {
public boolean[] distanceLimitedPathsExist(int n, int[][] edgeList, int[][] queries) {
Arrays.sort(edgeList, (a, b) -> a[2] - b[2]);
Integer[] index = new Integer[queries.length];
for (int i = 0; i < queries.length; i++) {
index[i] = i;
}
Arrays.sort(index, (a, b) -> queries[a][2] - queries[b][2]);
int[] uf = new int[n];
for (int i = 0; i < n; i++) {
uf[i] = i;
}
boolean[] res = new boolean[queries.length];
int k = 0;
for (int i : index) {
while (k < edgeList.length && edgeList[k][2] < queries[i][2]) {
merge(uf, edgeList[k][0], edgeList[k][1]);
k++;
}
res[i] = find(uf, queries[i][0]) == find(uf, queries[i][1]);
}
return res;
}
public int find(int[] uf, int x) {
if (uf[x] == x) {
return x;
}
return uf[x] = find(uf, uf[x]);
}
public void merge(int[] uf, int x, int y) {
x = find(uf, x);
y = find(uf, y);
uf[y] = x;
}
}
并查集完整模板:
class UF {
private int count;
private int[] parent;
private int[] size;
public UF(int n) {
this.count = n;
parent = new int[n];
size = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
size[i] = 1;
}
}
public void union(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
if (rootP == rootQ) return ;
// 平衡性优化
if (size[rootP] < size[rootQ]) {
parent[rootP] = rootQ;
size[rootQ] += size[rootP];
} else {
parent[rootQ] = rootP;
size[rootP] += size[rootQ];
}
this.count--;
}
public boolean conneted(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
return rootP == rootQ;
}
public int count() {
return this.count;
}
private int find(int x) {
// 路径压缩
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
}