并查集

union find

并查集是用于管理元素分组情况的数据结构,注意并查集虽然可以进行合并操作但是无法进行分割操作。主要有下面两个操作:

  • 查询两个元素是否在同一组
  • 合并两个元素所在的组

并查集操作

并查集使用树作为数据结构(不一定是二叉树)

合并

从一个组的根向另一个组的根连一条边。这里一定要注意,合并的时候是把两个节点所在的树合并到一起,而不仅仅是将一个节点连到另一个节点上。

查询

查询两个元素是否在同一组内,只需查找它们是否在同一个树内(从两个节点向上走看是否能走到同一个根)。

在具体实现中我们还有如下技巧:
一是避免退化(即根的子树在高度上有较大差异),对于每棵树我们记录高度,在合并时从高度小的树向高度大的树连边(让高度较小的树称为高度较大的树的子树)
而是路径压缩,当我们查询节点时,一旦这个节点走到了根节点我们就将该节点连接到根节点上,同时将查询过程中的所有节点全部连接到根节点上。

牛刀小试

朋友圈

本题思路十分简单,可以将所有学生看成节点,若任意两位学生之间是朋友关系,则进行合并操作,开始时所有节点各自独立,朋友圈的个数为所有学生人数,每发生一次合并操作,就减掉一个朋友圈,遍历整个矩阵即可得到结果。

class Solution {
	const int MAX_N = 1000; 
    int par[MAX_N];
    int  height[MAX_N]; 
public:
    int findCircleNum(vector<vector<int>>& M) {
        int total = M.size(); 
        init(total); 

        int sum = total; 
        for (int i = 0; i < total; i++) {
            for (int j = 0; j < total; j++) {
                if(i==j){
                    continue; 
                }
                if (M[i][j] == 1) {
                    unite(i, j, &sum);
                   // M[j][i] = -1; 
                }
                
            }
        }
        return sum; 
    }
    void init(int total)
    {
        for (int i = 0; i < total; i++) {
            par[i] = i; 
            height[i] = 0;
        }

    }
    int find(int value)  //寻找value的根节点
    {
        int res; 
        if (par[value] == value) {
            res = value; 
        }
        else {
            res = find(par[value]); 
        }
        par[value] = res;
        return res; 
    }
    void unite(int x, int y,int *sum)
    {
        x = find(x); 
        y = find(y); 

        if (x == y) {
            return;
        }
        (*sum)--; 
        if (height[x] > height[y]) {
            par[y] = x;  //将y的父节点连到x上

        }
        else {
            par[x] = y; 
            if (height[x] == height[y]) {
                height[x]++; 
            }
        }
    }
};

时间复杂度为 o ( n 2 ) o(n^2) o(n2),对二维数组进行了遍历。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值