并查集简要分析

并查集:(union-find sets)

一种简单的用途广泛的集合. 并查集是若干个不相交集合,能够实现较快的合并和判断元素所在集合的操作,应用很多,如其求无向图的连通分量个数等。最完美的应用当属:实现Kruskar算法求最小生成树。

并查集的精髓(即它的三种操作,结合实现代码模板进行理解):

1、MakeSet(x) 把每一个元素初始化为一个集合

初始化后每一个元素的父亲节点是它本身,每一个元素的祖先节点也是它本身(也可以根据情况而变)。

2、FindSet(x) 查找一个元素所在的集合

查找一个元素所在的集合,其精髓是找到这个元素所在集合的祖先!这个才是并查集判断和合并的最终依据。

判断两个元素是否属于同一集合,只要看他们所在集合的祖先是否相同即可。

合并两个集合,也是使一个集合的祖先成为另一个集合的祖先,具体见示意图

3、Union(x,y) 合并x,y所在的两个集合

合并两个不相交集合操作很简单:

利用Find_Set找到其中两个集合的祖先,将一个集合的祖先指向另一个集合的祖先。

并查集的优化

1、Find_Set(x)时 路径压缩

寻找祖先时我们一般采用递归查找,但是当元素很多亦或是整棵树变为一条链时,每次Find_Set(x)都是O(n)的复杂度,有没有办法减小这个复杂度呢?

答案是肯定的,这就是路径压缩,即当我们经过"递推"找到祖先节点后,"回溯"的时候顺便将它的子孙节点都直接指向祖先,这样以后再次Find_Set(x)时复杂度就变成O(1)了,如下图所示;可见,路径压缩方便了以后的查找。

2、Union(x,y)时 按秩合并

即合并的时候将元素少的集合合并到元素多的集合中,这样合并之后树的高度会相对较小。

int father[MAX];
int rank[MAX];

void MakeSet(int x)
{
	father[x] = x;
	rank[x] = 0;
}

int FindSet(int x)//递归实现路径压缩查找
	if (x!=father[x])
	{
		//在回溯的时候将祖先结点作为其他结点的父节点
		father[x] = FindSet(father[x]);
	}
	return father[x];
}

int FindSet(int x)//非递归实现路径压缩
{
	int root = x;
	while(root!=father[root])//找到根结点
		root = father[root];
	int tem = x;
	while(tem != x)//进行路径压缩,把根结点的值赋值给x的父节点
	{
		int tempFather = father[tem];
		father[tem] = root;
		tem = tempFather;
	}
	return root;
}

int union(int x,int y)
{
	x = FindSet(x);
	y = FindSet(y);
	if (x == y)
	{
		return 0;
	}
	//按秩合并,x的秩大说明高度比较高,更接近与根结点.
	//所以将x作为y的根结点
	if (rank[x]>rank[y])
	{
		father[y] = x;
	}
	else
	{
		if (rank[x] == rank[y])
		{
			rank[y] ++;
		}
		father[x] = y;
	}
	return 1;//返回1说明x和y不属于一个集合
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-14 01:17:33

并查集简要分析的相关文章

并查集扩展之最小生成树Kruskal算法

并查集有很多经典的应用.在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中. 其中一个非常经典的应用是最小生成树的Kruskal算法.给定一个具有n个节点的连通图,它的生成树是原图的一个子图,包含所有n个节点,且有保持图连通的最少的边(n-1条边).边权值最小的生成树是最小生成树. kruskal算法是一个贪心算法,把所有的边按权值从小到大依次考虑,如果当前边加进生成树中会出现回路

poj1182 并查集

题目连接:http://poj.org/problem?id=1182 基础并查集,需要维护与根节点关系,解析见代码: /* poj 1182 并查集 思路分析:让你分析这些话里面多少假的 只需要用一个并查集将已经给出的这些元素存起来 同时记录每个元素与他们根节点关系,如果根结点相同 但是关系不符合就是出现了矛盾. 关系有三种:同类 记为0 吃根节点 1 被根节点吃 2 这样也是为了与他给出的d关系一致 d-1就与我们规定的关系一致了 并查集的关键是路径压缩,在压缩路径的同时我们要更新与根节点关

【hiho】14 无间道之并查集【图论--并查集】

传送门:无间道之并查集 分析 并查集的分析可以看上面的传送门,写的挺好的了. 其实在我看来并查集就是一种方便的维护集合的一种技巧,提出了代表元素这一概念. My AC Code #include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1e5+5; int represent[maxn]; int find_represent(int x) {

【BZOJ3211】花神游历各国 树状数组 并查集 均摊分析

链接: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44686727"); } 题解: 一个点开几次方就没啦.所以我们只需要修改不是0或者1的点就行了. 均摊基本O(n). 然后用并查集维护一个点右边第一个不是0的数. 手写读入果然高大上.卡rank神器. 顺便Orz一下wys大神. 代

并查集分析与扩展

http://jarily.com/acm/2012/11/26/bcj.html 一 并查集的基础 1 基本功能 并查集用于处理不相交数据集合; 2 基本操作 (1)查找一个元素的祖先(即查找这个元素属于哪个集合); (2)将两个元素所在的集合合并为一个集合; (3)删除操作,即把某个元素从它所在的集合中删除; 3 基本代码 #include<iostream> #include<cstring> #include<cstdio> using namespace st

HDU 5441 离线处理 + 并查集

题意:给n个节点m条带权值边的无向图.然后q个问题,每次询问点对的数目,点对需要满足的条件是:1)连通:2)其路径的最大权值不能超过询问值. 分析:如果没次询问一次,dfs一次,很可能超时,因此可以用并查集.离线处理,把边按权值排序,把问题按大小排序.然后离线的过程就是不断向图中加边的过程. 比如样例如下: 然后离线处理,排完序后将会是一条一条的加边:问题也排了序,因此是个累加过程... 1 #include <cstdio> 2 #include <iostream> 3 #in

习题:过路费(kruskal+并查集+LCA)

过路费  [问题描述]在某个遥远的国家里,有 n 个城市.编号为 1,2,3,…,n.这个国家的政府修 建了 m 条双向道路,每条道路连接着两个城市.政府规定从城市 S 到城市 T 需 要收取的过路费为所经过城市之间道路长度的最大值.如:A 到 B 长度为 2,B 到 C 长度为 3,那么开车从 A 经过 B 到 C 需要上交的过路费为 3. 佳佳是个做生意的人,需要经常开车从任意一个城市到另外一个城市,因此 他需要频繁地上交过路费,由于忙于做生意,所以他无时间来寻找交过路费最低 的行驶路线.然

HDU3081Marriage Match II(二分答案+并查集+最大流SAP)经典

Marriage Match II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2507    Accepted Submission(s): 856 Problem Description Presumably, you all have known the question of stable marriage match. A

并查集问题

并查集问题主要用于方便地查询任意个体间有没有关系. 初始个体间的关系没有全局性,是局部.分散的,并查集问题要根据已有的局部分散关系分析得到全局关系. 核心思想:根据关系对个体进行分类.贴标签.把属于一类的都贴上一个标签,可以清楚地分辨哪一个是哪一类,不同的是不是同一类. 具体方法: 为了给同一类贴上一样的标签,涉及两个问题,一个是查询标签,一个是更新标签.查询标签是为了判断是否要合并为同类,更新标签是为了合并后将标签统一. 因为标签对同类个体是公共属性,要放在公共位置. 容易想到的是链表,可以指