2316. 统计无向图中无法互相到达点对数
给你一个整数 n
,表示一张 无向图 中有 n
个节点,编号为 0
到 n - 1
。同时给你一个二维整数数组 edges
,其中 edges[i] = [ai, bi]
表示节点 ai
和 bi
之间有一条 无向 边。
请你返回 无法互相到达 的不同 点对数目 。
示例 1:
输入:n = 3, edges = [[0,1],[0,2],[1,2]]
输出:0
解释:所有点都能互相到达,意味着没有点对无法互相到达,所以我们返回 0 。
示例 2:
输入:n = 7, edges = [[0,2],[0,5],[2,4],[1,6],[5,4]]
输出:14
解释:总共有 14 个点对互相无法到达:
[[0,1],[0,3],[0,6],[1,2],[1,3],[1,4],[1,5],[2,3],[2,6],[3,4],[3,5],[3,6],[4,6],[5,6]]
所以我们返回 14 。
并查集
并查集,搜索每个点对应的集合,n-该集合大小 就是该点不能到达的点的数目。由于a到b和b到a都统计了一次,所以最后需要 /2。
//并查集算法结构
class UnionFind{
private:
vector<int> parents;//记录每个节点父节点
vector<int> sizes;//记录每个节点对应的集合大小
public:
UnionFind(int n): parents(n), sizes(n,1){
iota(parents.begin(), parents.end(),0); //iota算法见下
}
//获取父节点,直至根节点
int Find(int x){
while(x!=parents[x]){
x=parents[x];
}
return x;
/*递归写法
if (parents[x] == x) {
return x;
}
return parents[x] = Find(parents[x]);
*/
}
//合并两结点对应的集合,并且进行路径优化
void Union(int x, int y){
int p_x=Find(x), p_y=Find(y);
if(p_x!=p_y){
if(sizes[p_x]>sizes[p_y]){
parents[p_y]=p_x;
sizes[p_x]+=sizes[p_y];
}else{
parents[p_x]=p_y;
sizes[p_y]+=sizes[p_x];
}
}
}
int GetSize(int x){
return sizes[Find(x)];
}
};
iota算法,它通过指定一个起始值,并根据区间的长度递增生成后续的值。它有助于快速生成递增的序列。
#include <iostream>
#include <numeric>
#include <vector>
int main() {
std::vector<int> nums(5);
std::iota(nums.begin(), nums.end(), 1);
for (const auto& num : nums) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
//输出
1 2 3 4 5
完整解法:
class UnionFind{
private:
vector<int> parents;
vector<int> sizes;
public:
UnionFind(int n): parents(n), sizes(n,1){
iota(parents.begin(), parents.end(),0);
}
//获取父节点,直至根节点
int Find(int x){
while(x!=parents[x]){
x=parents[x];
}
return x;
}
void Union(int x, int y){
int p_x=Find(x), p_y=Find(y);
if(p_x!=p_y){
if(sizes[p_x]>sizes[p_y]){
parents[p_y]=p_x;
sizes[p_x]+=sizes[p_y];
}else{
parents[p_x]=p_y;
sizes[p_y]+=sizes[p_x];
}
}
}
int GetSize(int x){
return sizes[Find(x)];
}
};
class Solution {
public:
long long countPairs(int n, vector<vector<int>>& edges) {
UnionFind uf(n);
for(const auto &edge:edges){
uf.Union(edge[0], edge[1]);
}
long long res=0;
for(int i=0;i<n;i++){
res+=n-uf.GetSize(i);
}
return res/2;
}
};
收获
学习了并查集的构造和使用方法,学习了 iota() 函数初始化数组。