leetcode:547、朋友圈

题目来源

题目描述

在这里插入图片描述

题目解析

题意

给出一个二维数组,它一定是个正方形。

  • 二维数组的索引可以看成是朋友圈的编号

  • 二维数组的值表示关系:1表示认识,0表示不认识

    • M[i][j] = 1:表示i认识j
    • (由题意)M[i][j] = 1,那么M[j][i]也为1,也就是j认识i
  • 也就是说:

    • 对角线一定全是1,因为自己是认识自己的
      • M[i][i] = 1
    • 而且这个矩阵是对称的
      • M[i][j] = 1
      • M[i][j] = 1
  • 求有几个朋友圈

并查集

在这里插入图片描述

根据二维数组生成一个并查集

刚开始时,有集合 ( 1 )、( 2 )、( 3 )、( 4 ) ({1})、({2})、({3})、({4}) 1)、(2)、(3)、(4

一行行遍历数组

  • 刚开始时是第一行:
    • 0和0是认识自己的,之前已经有了 ( 0 ) (0) (0),不用管
    • 0和1不认识,不用管
    • 0和2认识,因此 ( 0 ) (0) (0)合并 ( 2 ) ({2}) 2,得到 ( 0 , 2 ) {(0,2)} 02
    • 0和3不认识,不用管
    • 0和4认识,因此合并 ( 4 ) ({4}) 4,得到 ( 0 , 2 、 4 ) {(0,2、4)} 024
  • 然后第二行
    • 1和0不认识
    • 1和1认识,之前已经有了 ( 1 ) (1) (1),不用管
    • 1和2不认识
    • 1和3认识,合并 ( 1 )、( 3 ) ({1})、({3}) 1)、(3得到 ( 1 , 3 ) {(1,3)} 13
    • 1和4不认识
  • 然后第三行,对于2
    • 2和0认识:原来应该得到 ( 2 , 0 ) (2,0) (20),这个 ( 2 , 0 ) (2,0) (20)已经在第一行中出现过了,所以不用管
    • 2和1不认识,不用管
    • 2自己认识自己,不用管
  • 第四行
    • 3和1认识: ( 1 )、( 3 ) ({1})、({3}) 1)、(3之前已经合并了,不用管
  • 第五行,得到 ( 4 ) (4) (4)
    • 4和0, ( 0 )、( 4 ) ({0})、({4}) 0)、(4之前已经合并了,不用管

也就是说生成了两个集合: ( 0 , 2 、 4 )、( 1 , 3 ) {(0,2、4)、(1,3)} 024)、(13,所以朋友圈的个数是2

因为这个矩阵高度对称,所以根据这个集合生成并查集时,只遍历右上角就可以了。

总的来说:

  • 先根据N生成集合{0}、{1}、{2}、{3}
  • 遍历矩阵的右上角,如果发现M[i][j] == 1,那么就将i和j合并
  • 最终返回联通量
class Solution {
    class UnionFind{
    private:
        std::vector<int> parent; // parent[i] = k : i的父亲是k
        std::vector<int> size;   // size[i] = k : 如果i是代表节点,size[i]才有意义( i所在的集合大小是多少),否则无意义
        std::vector<int> help;  //  辅助结构
        int cnt;               //一共有多少个集合
    public:
        explicit UnionFind(int n){
            cnt = n;
            parent.resize(n);
            size.resize(n);
            help.resize(n);
            for (int i = 0; i < n; ++i) {
                parent[i] = i;
                size[i] = 1;
            }
        }

        // 返回i的代表节点
        // 这个过程要做路径压缩
        int findRoot(int i){
            int hi = 0;
            while (i != parent[i]){
                help[hi++] = parent[i];
                i = parent[i];
            }
            for (hi--; hi >= 0; --hi) {
                parent[help[hi]] = i;
            }
            return i;
        }
        
        void unionEle(int i, int j){
            int f1 = findRoot(i);
            int f2 = findRoot(j);
            if(f1 != f2){
                if(size[f1] >= size[f2]){
                    parent[f2] = f1;
                    size[f1] = size[f1] + size[f2];
                }else{
                    parent[f1] = f2;
                    size[f2] = size[f2] + size[f1];
                }
                --cnt;
            }
        }
        
        int counts() const{
            return cnt;
        }
    };
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        int N = isConnected.size();
        UnionFind unionFind(N);   //先初始化集合: {0} {1} {2} {N-1}
        // 遍历这个二维数组的右上角
        for (int i = 0; i < N; ++i) {
            for (int j = i + 1; j < N; ++j) {
                if(isConnected[i][j] == 1){  // i和j相互认识就合并
                    unionFind.unionEle(i, j);
                }
            }
        }
   
        // 有多少个集合就表示有多少个朋友圈
        return unionFind.counts();
    }
};


为什么要help数组,因为数组的操作比栈快多了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值