并查集

定义

利用一个数组实现并查集

int father[N];

其中father[i]表示元素i的父亲结点,比如father[1] = 2说明1的父亲结点是元素2;而如果father[i] == i则说明i是该集合的根节点。

father[1] = 1;
father[2] = 1;
father[3] = 2;
father[4] = 2;
父亲关系图:
     		 1
     		 |
     		 2
    		/ \
  		   3   4

基本操作

  1. 初始化
    刚开始每个元素都是独立的集合
for (int i = 0; i <= N; i++)
	father[i] = i;
  1. 查找
    因为一个集合只有一个根节点,所以查找就是对给定的结点找其根节点的过程,可以用递归或者递推。思路是反复寻找父亲结点,直到找到根节点(father[i] == i)
    递推:
int findFather(int x)
{
	while (x != father[x])
		x = father[x];
	return x;
}

递归:

int findFather(int x)
{
	if (x == father[x])
		return x;
	else
		return findFather(father[x]);
}
  1. 合并
    合并就是帮其中一个集合的根节点的父亲指向另一个集合的根节点。
  • 首先要确保两个集合不属于同一个集合,调用上面的findFather函数,对两个元素a、b分别查找根节点,然后再判断根节点是否相同
  • 利用上步找到的根节点faA和faB,因此只需要把其中一个父亲结点指向另一个结点,father[faA] = faB或者father[faB] = faA都行
void Union(int a, int b)
{
	int faA = findFather(a);
	int faB = findFather(b);
	if (faA != faB)
		father[faA] = faB;
}

路径压缩

如果元素很多并且连成一条链,那么查找函数效率就会很低,但是可以这样实现路径压缩:

  1                       1
  |       ——>           / | \
  2                    2  3  4
  |        
  3
  |
  4

将当前查询结点路径上所有结点的父亲都指向根结点,将复杂度优化到O(1)。

int findFather(int x)
{
	int a = x; //先保存x的值
	while (x != father[x]) //x指向父结点
		x = father[x];
	while (a != father[a])
	{
		int z = a; //z指向a
		a = father[a]; //a往上移
		father[z] = x; //将z的父结点变成根结点
	}
	return x;
}

递归写法:

int findFather(int x)
{
	if (x == father[x])
		return x;
	else
	{
		int F = findFather(father[x]);
		father[x] = F;
		return F;
	}
}

例题1:
在这里插入图片描述
有关联的好朋友都合并到同一个组中

#include <iostream>
#include <cstdlib>
using namespace std;
int father[101];
void init(int n);
void Union(int a, int b);
int findFather(int a);
bool isRoot(int a);
int main()
{
	int n, m;
	cin >> n >> m;
	init(n);
	for (int i = 0; i < m; i++)
	{
		int friA, friB;
		cin >> friA >> friB;
		Union(friA, friB); //合并到同一个组中
	}
	int cnt = 0;
	for (int i = 1; i <= n; i++)
		if (isRoot(i)) //最后数有几个根即可
			cnt++;
	cout << cnt << endl;
	system("pause");
	return 0;
}
void init(int n)
{
	for (int i = 1; i <= n; i++)
		father[i] = i;
}
int findFather(int a)
{
	while (father[a] != a)
		a = father[a];
	return a;
}
void Union(int a, int b)
{
	int faA = findFather(a);
	int faB = findFather(b);
	if (faA != faB)
		father[faA] = faB;
}
bool isRoot(int a)
{
	return findFather(a) == a;
}

例题2:PAT1107 Social Clusters

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值