数据结构与算法_并查集

概念

​ 并查集(Disjoint Sets),字面意思是一种集合。这个集合具有的功能是合并查找

​ 合并(Union):将两个集合合并成一个集合。

​ 查找(Find):判断两个元素是否在一个集合。

并查集使用数组就可以实现:parent[]。

图的环问题

如果是我们想要判断一个无向图是否存在环,我们应该如何做呢?

由这个问题正式引出并查集:

首先,以上提到的并查集是一个树状的结构,其中parent数组:parent[i] = j 的含义就是 parent of i is j; 即i的双亲是j。

我们通过一个树状的结构来判断图是否存在环:遍历边的过程中,只要有边两端的结点在同一个集合中,则必然有环。

其实,树是可以看出一个特殊的图。如果说一个图要变成一个树,他首先要去掉其中“多余”的边。其实这个并查集就是用一个树结构铺在图上,那么这个图中“多余”的边也就一目了然了。

或许还可以联系到就是一个重要最小生成树算法——Kruskal算法

并查集的基本操作

初试化双亲数组
for(int i = 0; i < n; i++)
	parent[i] = i;

实际上你赋初值为-1也可以。

查找

并查集本身是一个树结构,那么必然一个集合只有一个根节点。所以查找操作就是对于给定的节点寻找其根节点

//非递归实现
int FindParent(int x)
{
	while(x != parent[x])
		x = parent[x];
	return x;
}

//递归实现
int FindParent(int x)
{
	if(x == parent[x])
		return x;
	else 
		return FindParent(parent[x]);
}
合并

合并思路很简单:当我们要合并两个集合的时候,首先要判断这两个集合到底是不是两个集合。

  1. 通过查找方法来寻找两个根节点,看看是不是同一个根节点。如果不是则证明这是两个集合。
  2. 我们找到这两个根节点之后,要进行一个合并操作,其实就是让一个根节点指向另一个根节点。(本质上是一个认爹的过程)
void Union(int a, int b)
{
	int parent_a = FindParent(a);
	int parent_b = FindParent(b);
	if(parnent_a != parent_b)
		parent[parent_a] = parent_b;
}

路径压缩

问题引出

既然并查集是一个树型结构,那么必然存在一种我们在树中最喜闻乐见的一种情况——树退化成链表。

所以并查集也存在这种情况。我们给出的优化方案是很清楚的:把所有节点的双亲节点全部指向根节点,那我们查找的时候就会非常方便。

具体实现思路

找某一个结点x,把这个x到根节点路径上的所有节点的双亲节点全部修改成根节点。(在找的时候顺便就给他改了)

int FindParent(int x)
{
	int a = x;//先保存一下x
    while(x != parent[x])
        x = parent[x];
    while(a != parent[a])
    {
		int b = a;
        a = parent[a];
        parent[b] = x;
    }
    return x;
}

//递归
int FindParent(int v)
{
	if(v == parent[v])
        return v;
    else
    {
		int a = FindParent(parent[v]);
        parent[v] = a;
        return a;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值