图论中的并查集

本文详细介绍了并查集这一高效的数据结构,包括其基本概念、核心操作(makeSet、unionSet、find)及实现代码,并通过实例展示了如何应用于解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

来源  http://www.cnblogs.com/cyjb/p/UnionFindSets.html

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

使用并查集时,首先会存在一组不相交的动态集合 S={S1,S2,,Sk},一般都会使用一个整数表示集合中的一个元素。

每个集合可能包含一个或多个元素,并选出集合中的某个元素作为代表。每个集合中具体包含了哪些元素是不关心的,具体选择哪个元素作为代表一般也是不关心的。我们关心的是,对

于给定的元素,可以很快的找到这个元素所在的集合(的代表),以及合并两个元素所在的集合,而且这些操作的时间复杂度都是常数级的。

并查集的基本操作有三个:

  1. makeSet(s):建立一个新的并查集,其中包含 s 个单元素集合
  2. unionSet(x, y):把元素 x 和元素 y 所在的集合合并,要求 x 和 y 所在的集合不相交,如果相交则不合并。
  3. find(x):找到元素 x 所在的集合的代表,该操作也可以用于判断两个元素是否位于同一个集合,只要将它们各自的代表比较一下就可以了。
using namespace std;
const int MAXSIZE=500;
int uset[MAXSIZE];
int rank[MAXSIZE];
void makeSet(int size)
{
	for(int i=0;i<size;i++)
	{
		uset[i]=i;
		rank[i]=0;//当前的深度
	}
}

int find(int x)
{
	int p=x,t;
	while(uset[p]!=p) p=uset[p];
	while(x!=p){t=uset[x];uset[x]=p;x=t;}//使得一个集合的元素都指向根
	return x;
}

void unionset(int x,int y)
{
	if((x=find(x))==(y=find(y))) return;
	if(rank[x]>rank[y]) uset[y]=x;
	else
	{
		uset[x]=y;
		if(rank[x]==rank[y]) rank[y]++;
	}
}
实例:


代码如下:

int friends(int n,int m,int r[5][5])
{
	makeSet(n);
	set<int> i_set;
	int i,j;
	for(i=1;i<=n;i++)
		for(j=i+1;j<=n;j++)
		{
			if(r[i-1][j-1]==1)
			{
				unionset(i-1,j-1);
			}
		}
	int res=0;
	for(i=0;i<n;i++)
	{
		//i_set.push_back(uset[i]);
		i_set.insert(uset[i]);
	}
	return i_set.size();
}
int _tmain(int argc, _TCHAR* argv[])
{	int n=5;
	int m=3;
	int r[5][5]={0};
	r[0][1]=1;
	r[1][2]=1;
	r[3][4]=1;
	cout<<friends(n,m,r);
	return 0;
}




### Python 中图论并查集的实现教程 #### 1. 并查集的基本概念 并查集(Disjoint Set),也称为联合-查找数据结构,主要用于管理一组不相交的动态合。它的核心功能包括 **查找** 和 **合** 操作。通过这些操作,可以有效地解决连通性问题[^1]。 #### 2. 并查集的核心操作 以下是并查集中最重要的两个操作: - **Find**: 查找某个节点所属的合编号。 - **Union**: 合两个不同的合。 为了提高效率,并查集通常会采用两种优化策略: - **按秩合**:总是将较小树连接到较大树上,从而减少树的高度。 - **路径压缩**:在执行 `find` 操作时,将沿途经过的所有节点直接指向根节点,进一步降低树的高度[^3]。 #### 3. 并查集的Python实现 下面是一个标准的并查集实现代码,包含了路径压缩和按秩合的功能: ```python class UnionFind: def __init__(self, size): self.parent = list(range(size)) self.rank = [0] * size # 初始化秩为0 def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) # 路径压缩 return self.parent[x] def union(self, x, y): root_x = self.find(x) root_y = self.find(y) if root_x == root_y: return False # 按秩合 if self.rank[root_x] < self.rank[root_y]: self.parent[root_x] = root_y elif self.rank[root_x] > self.rank[root_y]: self.parent[root_y] = root_x else: self.parent[root_y] = root_x self.rank[root_x] += 1 return True ``` 上述代码定义了一个类 `UnionFind` 来封装并查集的操作逻辑。其中 `__init__` 方法初始化父节点数组和秩数组;`find` 方法实现了带路径压缩的查找操作;`union` 方法则完成了带有按秩合合合操作[^4]。 #### 4. 应用场景 并查集广泛应用于各种涉及连通性的算法问题中,比如最小生成树(Kruskal 算法)、社交网络分析以及岛屿数量计算等问题。例如,在 Kruskal 算法中,可以通过并查集快速判断两条边是否属于同一个连通分量[^2]。 --- ### 示例程序 假设我们需要在一个无向中检测是否存在环路,则可以用如下方法完成: ```python def has_cycle(edges, n_nodes): uf = UnionFind(n_nodes) for u, v in edges: if not uf.union(u, v): # 如果无法合说明形成环 return True return False # 测试输入 edges = [(0, 1), (1, 2), (2, 0)] n_nodes = 3 print(has_cycle(edges, n_nodes)) # 输出: True 表示存在环 ``` 此函数利用了并查集来逐条加入边,检查每一步是否会引入新的环路。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值