并查集解释

并查集是算法中非常重要的概念也是常用的一个结构
网上看到我觉得最好的是https://blog.csdn.net/niushuai666/article/details/6662911,也就是我参考的博文
我用自己的语言删减了一下进行记录。

并查集的概念

并查集可以生动的用下图表示。
可以看到是一颗颗的树(图),任意两点可以相互访问的是一类
一般来说,每个点只知道自己的父节点是什么。最远祖先(树的根节点)的父节点是自身。最远祖先相同的两个节点属于同一个集合。
两个集合合并,只需要两个点分别来自两个集合,彼此可以访问,那两个集合就都联通了。

在这里插入图片描述

算法实现

那么算法需要实现的部分:
pre[]数组: 用于储存这个节点的父节点。
int uninonsearch(int root) :查找根节点
void join(int root1, int root2) :将root1和root2所属的两个集合合并
因为树可能会变得特别深,不利于查找,所以需要一个路径压缩算法实现下图:其实就是在寻找根节点过程中途径的所有节点的父亲设置为根节点。
在这里插入图片描述

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int pre[1010]; //记录每个点的父节点
int unionsearch(int root)
{
	int son, tmp;
	son = root;
	while(root != pre[root]) //寻找根节点
		root = pre[root];
	while(son != root) //已经找到了根节点,进行路径压缩
	{
		tmp = pre[son];
		pre[son] = root;
		son = tmp;
	}
	return root; //返回根节点
}
void join(int root1, int root2) //合并函数
{
	int x, y;
	x = unionsearch(root1);//找到根节点
	y = unionsearch(root2);//找到根节点
	if(x != y) 
		pre[x] = y; //合并,就是其中一个的父节点换为另一个
}

int main()
{
	int num, road, total, i, start, end, root1, root2;
	while(scanf("%d%d", &num, &road) && num)
	{
		total = num - 1; //共num-1个门派
		for(i = 1; i <= num; ++i) //每条路都是掌门(自己自成一棵树)
			pre[i] = i;
		while(road--)
		{
			scanf("%d%d", &start, &end); //他俩要结拜
			root1 = unionsearch(start);
			root2 = unionsearch(end);
			if(root1 != root2) //掌门不同?合并!
			{
				pre[root1] = root2;
				total--; //门派少一个,总的集合数减一
			}
		}
		printf("%d\n", total);//天下局势:还剩几个门派
	}
	return 0;
}

实例

题目链接
描述:
某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?

输入:
测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,也就是说
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。

解释:
首先根据城市数量初始化有N个集合(每个城市自身为一个)。根据有相互之间有路,来合并集合,最后得到还剩下多少集合(T个)。那就需要建立T-1条路。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章