3493. 属性图
给你一个二维整数数组 properties
,其维度为 n x m
,以及一个整数 k
。
定义一个函数 intersect(a, b)
,它返回数组 a
和 b
中 共有的不同整数的数量 。
构造一个 无向图,其中每个索引 i
对应 properties[i]
。如果且仅当 intersect(properties[i], properties[j]) >= k
(其中 i
和 j
的范围为 [0, n - 1]
且 i != j
),节点 i
和节点 j
之间有一条边。
返回结果图中 连通分量 的数量。
示例 1:
输入: properties = [[1,2],[1,1],[3,4],[4,5],[5,6],[7,7]], k = 1
输出: 3
解释:
生成的图有 3 个连通分量:
示例 2:
输入: properties = [[1,2,3],[2,3,4],[4,3,5]], k = 2
输出: 1
解释:
生成的图有 1 个连通分量:
示例 3:
输入: properties = [[1,1],[1,1]], k = 2
输出: 2
解释:
intersect(properties[0], properties[1]) = 1
,小于 k
。因此在图中 properties[0]
和 properties[1]
之间没有边。
提示:
1 <= n == properties.length <= 100
1 <= m == properties[i].length <= 100
1 <= properties[i][j] <= 100
1 <= k <= m
原站题解
python3 解法, 执行用时: 235 ms, 内存消耗: 18.3 MB, 提交时间: 2025-03-24 23:38:53
class UnionFind: def __init__(self, n: int): # 一开始有 n 个集合 {0}, {1}, ..., {n-1} # 集合 i 的代表元是自己 self._fa = list(range(n)) # 代表元 self.cc = n # 连通块个数 # 返回 x 所在集合的代表元 # 同时做路径压缩,也就是把 x 所在集合中的所有元素的 fa 都改成代表元 def find(self, x: int) -> int: # 如果 fa[x] == x,则表示 x 是代表元 if self._fa[x] != x: self._fa[x] = self.find(self._fa[x]) # fa 改成代表元 return self._fa[x] # 把 from 所在集合合并到 to 所在集合中 def merge(self, from_: int, to: int) -> None: x, y = self.find(from_), self.find(to) if x == y: # from 和 to 在同一个集合,不做合并 return self._fa[x] = y # 合并集合。修改后就可以认为 from 和 to 在同一个集合了 self.cc -= 1 # 合并后,连通块个数减少了 1 class Solution: def numberOfComponents(self, properties: List[List[int]], k: int) -> int: sets = list(map(set, properties)) uf = UnionFind(len(properties)) for i, a in enumerate(sets): for j, b in enumerate(sets[:i]): if len(a & b) >= k: uf.merge(j, i) return uf.cc
python3 解法, 执行用时: 66 ms, 内存消耗: 18.1 MB, 提交时间: 2025-03-24 23:38:37
class UnionFind: def __init__(self, n: int): # 一开始有 n 个集合 {0}, {1}, ..., {n-1} # 集合 i 的代表元是自己 self._fa = list(range(n)) # 代表元 self.cc = n # 连通块个数 # 返回 x 所在集合的代表元 # 同时做路径压缩,也就是把 x 所在集合中的所有元素的 fa 都改成代表元 def find(self, x: int) -> int: # 如果 fa[x] == x,则表示 x 是代表元 if self._fa[x] != x: self._fa[x] = self.find(self._fa[x]) # fa 改成代表元 return self._fa[x] # 把 from 所在集合合并到 to 所在集合中 def merge(self, from_: int, to: int) -> None: x, y = self.find(from_), self.find(to) if x == y: # from 和 to 在同一个集合,不做合并 return self._fa[x] = y # 合并集合。修改后就可以认为 from 和 to 在同一个集合了 self.cc -= 1 # 合并后,连通块个数减少了 1 class Solution: def numberOfComponents(self, properties: List[List[int]], k: int) -> int: sets = [reduce(or_, (1 << x for x in a)) for a in properties] uf = UnionFind(len(properties)) for i, a in enumerate(sets): for j, b in enumerate(sets[:i]): if (a & b).bit_count() >= k: uf.merge(j, i) return uf.cc
java 解法, 执行用时: 232 ms, 内存消耗: 44.9 MB, 提交时间: 2025-03-24 23:38:15
class UnionFind { private final int[] fa; public int cc; // 连通块个数 UnionFind(int n) { fa = new int[n]; cc = n; for (int i = 0; i < n; i++) { fa[i] = i; } } public int find(int x) { if (fa[x] != x) { fa[x] = find(fa[x]); } return fa[x]; } public void merge(int from, int to) { int x = find(from); int y = find(to); if (x == y) { return; } fa[x] = y; cc--; } } class Solution { public int numberOfComponents(int[][] properties, int k) { int n = properties.length; int m = properties[0].length; Set<Integer>[] sets = new HashSet[n]; Arrays.setAll(sets, i -> new HashSet<>(m)); for (int i = 0; i < n; i++) { for (int x : properties[i]) { sets[i].add(x); } } UnionFind u = new UnionFind(n); for (int i = 0; i < n; i++) { Set<Integer> a = sets[i]; for (int j = 0; j < i; j++) { int cnt = 0; for (int x : sets[j]) { if (a.contains(x)) { cnt++; } } if (cnt >= k) { u.merge(i, j); } } } return u.cc; } }
cpp 解法, 执行用时: 189 ms, 内存消耗: 48.7 MB, 提交时间: 2025-03-24 23:38:02
class UnionFind { vector<int> fa; public: int cc; // 连通块个数 UnionFind(int n) : fa(n), cc(n) { ranges::iota(fa, 0); } int find(int x) { if (fa[x] != x) { fa[x] = find(fa[x]); } return fa[x]; } void merge(int from, int to) { int x = find(from), y = find(to); if (x == y) { return; } fa[x] = y; cc--; } }; class Solution { public: int numberOfComponents(vector<vector<int>>& properties, int k) { int n = properties.size(); vector<unordered_set<int>> sets(n); for (int i = 0; i < n; i++) { sets[i] = unordered_set(properties[i].begin(), properties[i].end()); } UnionFind uf(n); for (int i = 0; i < n; i++) { for (int j = 0; j < i; j++) { int cnt = 0; for (int x : sets[j]) { if (sets[i].contains(x)) { cnt++; } } if (cnt >= k) { uf.merge(i, j); } } } return uf.cc; } };
golang 解法, 执行用时: 293 ms, 内存消耗: 8.6 MB, 提交时间: 2025-03-24 23:37:49
type uf struct { fa []int cc int } func newUnionFind(n int) uf { fa := make([]int, n) for i := range fa { fa[i] = i } return uf{fa, n} } func (u uf) find(x int) int { if u.fa[x] != x { u.fa[x] = u.find(u.fa[x]) } return u.fa[x] } func (u *uf) merge(from, to int) { x, y := u.find(from), u.find(to) if x == y { return } u.fa[x] = y u.cc-- } func numberOfComponents(properties [][]int, k int) int { sets := make([]map[int]bool, len(properties)) for i, a := range properties { sets[i] = map[int]bool{} for _, x := range a { sets[i][x] = true } } u := newUnionFind(len(properties)) for i, a := range sets { for j, b := range sets[:i] { cnt := 0 for x := range b { if a[x] { cnt++ } } if cnt >= k { u.merge(i, j) } } } return u.cc }