【每日一题】2316. 统计无向图中无法互相到达点对数


2316. 统计无向图中无法互相到达点对数

给你一个整数 n ,表示一张 无向图 中有 n 个节点,编号为 0n - 1 。同时给你一个二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示节点 aibi 之间有一条 无向 边。

请你返回 无法互相到达 的不同 点对数目

示例 1:

img

输入:n = 3, edges = [[0,1],[0,2],[1,2]]
输出:0
解释:所有点都能互相到达,意味着没有点对无法互相到达,所以我们返回 0 。

示例 2:

img

输入: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 。

详见: 2316. 统计无向图中无法互相到达点对数


并查集

并查集,搜索每个点对应的集合,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() 函数初始化数组。

C++ 数据结构专题 - 并查集(一)
【C++11算法】iota算法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值