并查集 union find

https://www.cnblogs.com/hapjin/p/5478352.html

https://segmentfault.com/a/1190000004023326

关于并查集的基本概念,可以参考这篇通俗有趣的博文:https://blog.csdn.net/u013546077/article/details/64509038

并查集的数据结构(父节点数组+两个函数)

并查集(Union/Find)从名字可以看出,主要包括两种基本操作:合并和查找。这说明,初始时并查集中的元素是不相交的,经过一系列的基本操作(Union),最终合并成一个大的集合。
而在某次合并之后,有一种合理的需求:某两个元素是否已经处在同一个集合中了?因此就需要Find操作。

并查集的数据可以用两个数组表示,数组下标是该元素的序号,第一个数组存储它的父节点,第二个数组存储节点深度(主要用于路径压缩,防止形成退化的长条形树)。

//并查集
int parent[N];    //存储父节点
int rank[N];      //存储节点深度

或者可以把节点本身和它的深度、父节点合并成一个结构体Node.

struct Node{
    int rank;
    int parent;
 }node[N];

除了数据,还需要两个操作函数:find和unionSet.

int find(int x);            //查
void unionSet(int x,int y); //并


并查集是一种 不相交集合 的数据结构,设有一个动态集合S={s1,s2,s3,.....sn},每个集合通过一个代表来标识,代表 就是动态集合S 中的某个元素。
比如,若某个元素 x 是否在集合 s1 中(Find操作),返回集合 s1 的代表元素即可。这样,判断两个元素是否在同一个集合中也是很方便的,只要看find(x) 和 find(y) 是否返回同一个代表即可。
为什么是动态集合S呢?因为随着Union操作,动态集合S中的子集合个数越来越少
数据结构的基本操作决定了它的应用范围,对并查集而言,一个简单的应用就是判断无向图的连通分量个数,或者判断无向图中任何两个顶点是否连通。

在合并和查找的过程中,需要考虑路径压缩问题。合并算法的最简单实现是分别找到x和y的根节点,然后把其中一个根节点变成另一个根节点的子节点。但这样下去很容易生成下面作图所示的长条形树。这种退化的树会让查找的时间复杂度骤升(可以达到O(N))。为了能避免这种退化树的产生,我们可以把一个集合中每个元素的父节点都设为代表节点,即让他们都直接指向最后的根节点。但这样虽然能极大地减少查询操作的时间复杂度,但却增加了合并操作的负担。两个集合合并时必须把其中一个集合中的所有元素的父节点都改成新的根节点(可以达到O(N))。 

最后,我们采用了一种很巧妙的办法:合并操作仍然和以前一样,只需要把一个根节点的父节点改成另一个根节点。但是,在此之前我们先判断这两个根节点的深度(即两个树的高度),将深度大的那个根节点作为新的根节点,这样的话能压制新树的高度。查询操作也和以前一样,向上层层查找到根节点。但是,在此之后需要把这条查询路径上的所有结点的父节点都改成根节点。

我们把更改树的结构使其更健康的重任平均分摊给了合并和查询两个操作。

查找函数

向上层层查找到根节点。但是,在此之后需要把这条查询路径上的所有结点的父节点都改成根节点。(以下图为例,查询6的根节点,第一步查到根节点为1,第二步将查询路径中的6、10、 8的父节点全部改为1)

	int find(int x) {
		int root = x;
		while (root != parent[root]) root = parent[root];//查找根节点
		while (root != x) {//将路径上的所有节点的父节点刷新为根节点
			int tmp = parent[x];
			parent[x] = root;
			x = tmp;
		}
		return root;
	}

合并函数

void unionSet(int x, int y) {
		int root1 = find(x), root2 = find(y);
		int rank1 = rank[x], rank2 = rank[y];		
		if (rank1 > rank2) {
			parent[root2] = root1;//根2挂在根1上
		}
		else  {
			parent[root1] = root2;//根1挂在根2上
			if (rank1 == rank2) {//若两树的高度相同,则新树的高度是原树高度再加一
				rank[root2] = rank1 + 1;
			}
		}
	}

并查集的应用:Kruskal算法求最小生成树

猜你喜欢

转载自blog.csdn.net/sinat_38972110/article/details/82089963