图论Graph Theory(3):并查集

一、并查集概念

并查集: 并查集是一种树型的数据结构,用于处理一些不交集(Disjoint Sets)的合并及查询问题。

二、并查集的两种操作

2.1 find()判断两个元素是否属于同一个集合
int find(int parent[], int i) 
{ 
	//parent存储i节点的父节点,parent[i]==i即为根节点
	if (parent[i] == i) 
		return i; 
	return find(parent, parent[i]); 
} 
2.2 union()合并两个不相交的元素
void Union(int parent[], int x, int y) 
{ 
	int xset = find(parent, x); //x的集合
	int yset = find(parent, y); //y的集合
	//若两个集合不相等
	//简单粗暴将两个集合合并
	if(xset != yset) 
	{ 
		parent[xset] = yset; 
	} 
} 

三、并查集的优化

3.1 路径压缩

显然上述查询中,如果简单粗暴的合并,该树可能变成线性的,查询时间将变成O(n)
所以我们在查找某一结点的根节点时候,顺便把他的子孙直接与其根节点相连,是树变扁,这样极大的提高了查找效率,变为O(logn)

int find(struct subset subsets[], int i) 
{ 
	// 发现根节点并且将节点直接指向根节点,根节点指向自己
	if (subsets[i].parent != i) 
		subsets[i].parent = find(subsets, subsets[i].parent); 

	return subsets[i].parent; 
} 
3.2 按秩优化

同上面的道理,希望树的深度尽量小,所以合并时将元素所在深度小的集合合并到元素所在深度大的集合。这里用rank(秩)代表树的高度

void Union(struct subset subsets[], int x, int y) 
{ 
	int xroot = find(subsets, x); 
	int yroot = find(subsets, y); 

	//rank小的子集连接到较大的子集
	if (subsets[xroot].rank < subsets[yroot].rank) 
		subsets[xroot].parent = yroot; 
	else if (subsets[xroot].rank > subsets[yroot].rank) 
		subsets[yroot].parent = xroot; 

	// 如果相同连接rank+1,显然相同高度的树连接到一起,高度自然+1
	else
	{ 
		subsets[yroot].parent = xroot; 
		subsets[xroot].rank++; 
	} 
} 

四、并查集判环

如果两个节点在同一个集合,那么说明有环

for(int e = 0; e < E; ++e) 
	{ 
		int x = find(subsets, graph->edge[e].src); 
		int y = find(subsets, graph->edge[e].dest); 

		if (x == y) 
			return true; 

		Union(subsets, x, y); 
	} 

五、完整代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;

struct Edge 
{ 
	int src, dest; 
}; 

struct Graph 
{ 
	// V个节点,E条边
	int V, E;  
	struct Edge* edge; 
}; 

struct subset 
{ 
	int parent; 
	int rank; 
}; 
 
struct Graph* createGraph(int V, int E) 
{ 
	struct Graph* graph = (struct Graph*) malloc( sizeof(struct Graph) ); 
	graph->V = V; 
	graph->E = E; 

	graph->edge = (struct Edge*) malloc( graph->E * sizeof( struct Edge ) ); 

	return graph; 
} 
 
int find(struct subset subsets[], int i) 
{ 
	if (subsets[i].parent != i) 
		subsets[i].parent = find(subsets, subsets[i].parent); 

	return subsets[i].parent; 
} 

void Union(struct subset subsets[], int x, int y) 
{ 
	int xroot = find(subsets, x); 
	int yroot = find(subsets, y); 

	if (subsets[xroot].rank < subsets[yroot].rank) 
		subsets[xroot].parent = yroot; 
	else if (subsets[xroot].rank > subsets[yroot].rank) 
		subsets[yroot].parent = xroot; 
 
	else
	{ 
		subsets[yroot].parent = xroot; 
		subsets[xroot].rank++; 
	} 
} 

bool isCycle( struct Graph* graph ) 
{ 
	int V = graph->V; 
	int E = graph->E; 

	struct subset *subsets = 
		(struct subset*) malloc( V * sizeof(struct subset) ); 

	for (int v = 0; v < V; ++v) 
	{ 
		subsets[v].parent = v; 
		subsets[v].rank = 0; 
	} 

	for(int e = 0; e < E; ++e) 
	{ 
		int x = find(subsets, graph->edge[e].src); 
		int y = find(subsets, graph->edge[e].dest); 

		if (x == y) 
			return 1; 

		Union(subsets, x, y); 
	} 
	return 0; 
} 

int main() 
{ 

	int V = 3, E = 3; 
	struct Graph* graph = createGraph(V, E); 
 
	graph->edge[0].src = 0; 
	graph->edge[0].dest = 1; 

	graph->edge[1].src = 1; 
	graph->edge[1].dest = 2; 
 
	graph->edge[2].src = 0; 
	graph->edge[2].dest = 2; 

	if (isCycle(graph)) 
		printf( "Contains cycle" ); 
	else
		printf( "Doesn't contain cycle" ); 

	return 0; 
} 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值