【数据结构和算法】并查集

定义

并查集,顾名思义,主要功能是合并查找,主要处理一些集合的合并问题。假设有N个元素,它们分别属于一些集合,每个元素最多只能属于一个集合,每个集合都有一个“祖先”,这些集合就构成一个并查集。

并查集的表示方式很简单,只需要一个数组v(数组的大小是元素的个数),对每个元素iv[i]表示它的父节点或祖先节点(如果i本身是某个集合的祖先节点,则v[i]==i)。下图的并查集包括4个集合,v=[0,1,2,2,2,5,5,5,5,5]
在这里插入图片描述
进一步地,数组counts表示以每个元素为祖先的集合大小。以上图为例,counts=[1,1,3,0,0,5,0,0,0,0]
通常初始化每个元素自成一个集合,所有集合的大小都是1

class UnionFind {
private:
	vector<int> v;
    vector<int> counts;
};

实现

并查集主要有两个方法:查找和合并。

查找

查找并返回元素x的祖先节点。

int UnionFind::Find(int x) {
    int anc = x;
    while (v[anc] != anc) {
        anc = v[anc];
    }
    return anc;
}

合并

如果某2个元素不属于同一集合,且需要合并它们,将小集合合并到大集合里。
xy是它们各自所属集合的祖先节点。

void UnionFind::Merge(int x, int y) {
    int anc1 = (counts[x] > counts[y]) ? x : y;
    int anc2 = (counts[x] <= counts[y]) ? x : y;
    v[anc2] = anc1;
    counts[anc1] += counts[anc2];
    counts[anc2] = 0;
}

朋友圈问题

题目

题目来源:leetcode 547:朋友圈
班上有N名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知AB的朋友,BC的朋友,那么我们可以认为A也是C的朋友。所谓的朋友圈,是指所有朋友的集合。

给定一个N * N的矩阵M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第i个和j个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

解题思路

使用并查集的思路:
(1)开始时每个学生自成一个集合。
(2)遍历矩阵M,根据学生之间的关系及各自的祖先节点,判断是否要合并集合(修改数组vcounts)。由于矩阵M是对称的,所以只需遍历一半。
(3)最后,通过数组vcounts统计集合的数量。

代码实现

class UnionFind {
public:
	int Find(int x);
	void Merge(int x, int y);
	int findCircleNum(vector<vector<int>>& M) { // 朋友圈问题
        int n = M.size();
        if (n == 0) {
            return n;
        }
        counts = vector<int>(n, 1);
        v = vector<int>(n);
        for (int i = 0; i < n; ++i) {
            v[i] = i;
        }

        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                if (M[i][j] == 1) {
                    int ancI = Find(i);
                    int ancJ = Find(j);
                    if (ancI != ancJ) {
                        Merge(ancI, ancJ);
                    }
                }
            }
        }

        int ans = 0;
        for (int i = 0; i < n; ++i) {
            if (counts[i] != 0) {
                ++ans;
            }
        }
        return ans;
    }
private:
	vector<int> v;
    vector<int> counts;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值