朋友圈

朋友圈

前言

本题所用的主要算法是并查集

并查集(Union-find Sets)是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题。一些常见的用途有求连通子图、求最小生成树的 Kruskal 算法和求最近公共祖先(Least Common Ancestors, LCA)等。

1.并查集的代码实现

并查集主要的实现方法是通过父节点数组vector f() 来实现父亲节点和儿子节点的联系,比如 f[3]=4 就是说3的父亲节点是4,而并查集想要完成的是将目标节点放在一个集合中,那就可以将他们的根节点(也可以称之为祖先节点)都变成一样的,这样在判断集合的时候,只要通过find函数(返回该节点祖先节点的函数)就可以找到祖先,而在同一个祖先下的节点就归在一个集合中,merge函数就是将两个节点的祖先节点设置为一个,从而做到将两个节点合并到同一个集合中。

vector<int> f(30001)

int find(int p)
{
	if (p == f[p])
		return p;
	else {
		return find(f[p]);
	}
}

void merge(int x, int y)
{
	int a = find(x), b = find(y);
	if (a != b)
		f[a] = b;
}

2.本题的解决代码

本题是一个并查集的经典例题,只要将并查集理解解决本题就很容易了,所以具体的解决步骤在此不再赘述。

#include<iostream>
#include<vector>

using namespace std;

vector<int> f(30001), cnt(30001), a(30001);

int find(int p)
{
	if (p == f[p])
		return p;
	else {
		return find(f[p]);
	}
}

void merge(int x, int y)
{
	int a = find(x), b = find(y);
	if (a != b)
		f[a] = b;
}

int main()
{
	int N, M;
	cin >> N >> M;
	for (int i = 1;i <= N;i++)
		f[i] = i;
	while (M--)
	{
		int x;
		cin >> x;
		for (int i = 0;i < x;i++)
			cin >> a[i];
		for (int i = 1;i < x;i++)
			merge(a[0], a[i]);
	}
	int max = 0;
	for (int i = 1;i <= N;i++)
	{
		int u = find(i);
		cnt[u]++;
		if (cnt[u] > max)
			max = cnt[u];
	}
	cout << max;
	return 0;
}

3.并查集的路径优化算法

在我们做题的时候其实不难发现,我们在判断一个人的根节点时,每判断一个人就需要从这个人开始一直遍历到他的祖先节点,而我们可以假设,后面的节点有一个是这个节点的父系节点(意思就是这个节点和他的祖先节点之间的一个节点),寻找这个节点的根节点的时候一样需要一直遍历到根节点,而很显然上述两个节点在通过find函数寻找根节点的过程中走了重复的路径,这就造成了时间效率的浪费,在一些时间复杂度要求严格的题目里就无法通过,于是就产生了路径压缩算法。

算法代码如下:

int find(int p)
{
	if (p == f[p])
		return p;
	else {
		return f[p] = find(f[p]);
	}
}

我们会发现其实压缩和不压缩在代码上区别不大,就是在返回值上做了一些改动

原本是:

return find(f[p]);

现在是:

return f[p] = find(f[p]);

这样可以省去之前节点已经走过的路程,每对一个节点进行find操作,就会将此节点的所有父系节点(就是从此节点到根节点之间的所有节点)他们的父亲节点全部设置为根节点,接下来如果碰到此节点的父系节点的find操作就可以一步找到根节点。

关于并查集还有非递归的实现方法,感兴趣的同学可以自己去学习一下。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值