带阈值的图连通性
难度:
标签:
题目描述
有 n
座城市,编号从 1
到 n
。编号为 x
和 y
的两座城市直接连通的前提是: x
和 y
的公因数中,至少有一个 严格大于 某个阈值 threshold
。更正式地说,如果存在整数 z
,且满足以下所有条件,则编号 x
和 y
的城市之间有一条道路:
x % z == 0
y % z == 0
z > threshold
给你两个整数 n
和 threshold
,以及一个待查询数组,请你判断每个查询 queries[i] = [ai, bi]
指向的城市 ai
和 bi
是否连通(即,它们之间是否存在一条路径)。
返回数组 answer
,其中answer.length == queries.length
。如果第 i
个查询中指向的城市 ai
和 bi
连通,则 answer[i]
为 true
;如果不连通,则 answer[i]
为 false
。
示例 1:
输入:n = 6, threshold = 2, queries = [[1,4],[2,5],[3,6]] 输出:[false,false,true] 解释:每个数的因数如下: 1: 1 2: 1, 2 3: 1, 3 4: 1, 2, 4 5: 1, 5 6: 1, 2, 3, 6 所有大于阈值的的因数已经加粗标识,只有城市 3 和 6 共享公约数 3 ,因此结果是: [1,4] 1 与 4 不连通 [2,5] 2 与 5 不连通 [3,6] 3 与 6 连通,存在路径 3--6
示例 2:
输入:n = 6, threshold = 0, queries = [[4,5],[3,4],[3,2],[2,6],[1,3]] 输出:[true,true,true,true,true] 解释:每个数的因数与上一个例子相同。但是,由于阈值为 0 ,所有的因数都大于阈值。因为所有的数字共享公因数 1 ,所以所有的城市都互相连通。
示例 3:
输入:n = 5, threshold = 1, queries = [[4,5],[4,5],[3,2],[2,3],[3,4]] 输出:[false,false,false,false,false] 解释:只有城市 2 和 4 共享的公约数 2 严格大于阈值 1 ,所以只有这两座城市是连通的。 注意,同一对节点 [x, y] 可以有多个查询,并且查询 [x,y] 等同于查询 [y,x] 。
提示:
2 <= n <= 104
0 <= threshold <= n
1 <= queries.length <= 105
queries[i].length == 2
1 <= ai, bi <= cities
ai != bi
代码结果
运行时间: 88 ms, 内存: 39.2 MB
/*
思路:
1. 使用并查集(Union-Find)来处理连通性问题。
2. 首先初始化并查集。
3. 遍历所有的数对(x, y),如果它们的公因数 z > threshold,则将它们合并。
4. 对于每个查询,判断两个城市是否在同一个集合中。
使用Java Stream API进行处理。
*/
import java.util.stream.IntStream;
public class Solution {
class UnionFind {
private int[] parent;
private int[] rank;
public UnionFind(int n) {
parent = new int[n + 1];
rank = new int[n + 1];
IntStream.range(0, n + 1).forEach(i -> parent[i] = i);
}
public int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
public void union(int x, int y) {
int rootX = find(x);
int 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]++;
}
}
}
}
public boolean[] areConnected(int n, int threshold, int[][] queries) {
UnionFind uf = new UnionFind(n);
IntStream.range(threshold + 1, n + 1).forEach(i -> {
IntStream.iterate(2 * i, j -> j <= n, j -> j + i).forEach(j -> uf.union(i, j));
});
return IntStream.range(0, queries.length)
.mapToObj(i -> uf.find(queries[i][0]) == uf.find(queries[i][1]))
.mapToBoolean(Boolean::booleanValue)
.toArray();
}
}
解释
方法:
该题解采用了并查集的思想来判断城市之间的连通性。首先,特判当阈值为0时,任意两个自然数都有公约数1,因此所有城市都连通。然后,使用并查集的方法,将每个城市初始化为独立的集合。接着,从阈值+1开始遍历到n,对于每个数p,遍历其所有大于阈值的倍数,并将这些倍数与p进行合并,表示它们之间是连通的。最后,对于每个查询,判断两个城市是否属于同一个集合,即可得到它们是否连通。
时间复杂度:
O(nlogn + mlogn)
空间复杂度:
O(n)
代码细节讲解
🦆
在题解中提到,如果阈值为0,则任何两个城市都连通。这是基于什么数学原理?
▷🦆
题解中提到从阈值+1开始遍历到n,为什么选择这个范围?在这个范围外的数有什么不同?
▷🦆
题解使用并查集处理连通性问题,但是如何确保初始化每个城市为独立集合后的处理是正确的?
▷🦆
题解中并查集的合并操作是如何确保所有必要的城市都被合并的?是否可能遗漏某些应该被合并的城市?
▷