目录
并查集的用途
并查集大多用在寻找同一类相关元素的题目中,更加通俗的讲,就是存在不同种类或者分类的题目中。例如给定某图,让你计算存在几个不相邻的区域。又比如给定一个代表人的序列,让你确定这个人群中存在几个小团体等等。这些问题都离不开其核心的东西,就是“不同类”。并查集就是在这种不同类问题中寻找相同关系的集群,进而进行其他操作。
并查集的思路
并查集的思路很清晰,也很简单,但是我见网上有很多关于并查集的超详细的讲解,个人认为,这么详细的讲解,对于并查集而言,没有必要。我在这理清一下并查集的思路,你就会很快地理解它。在用途中说过,并查集一般用在“不同类”的问题中,所以它存在一个“找关系”的特点。说白的,并查集其实就是将群集中的元素分为各个派别,在每个派别中存在上下级的关系,自然而然地,每个派别需要一个老大,而这个老大就被当成这个派别的首领(也就是标志)。之后你在判断两个元素是否是同一个“派别”的时候,只要判断它们的老大是否是同一个人就行,而这就是并查集的思想。
详解代码
说到这,你可能有那么一点感觉了,这里我在加上代码的讲解,你就会一目了然了。
const int Max = 100;
//ves数组的下标代表群集中的每个元素,儿数组中存放的就是
//每个元素的上级,如果一个元素的上级是它自己,就说明这个
//元素是某一个“派别”的老大
int ves[Max];
/*
find函数就是用于找到每个元素的老大是否同一个的函数
*/
int find(int k)
{
int temp = k;
while (temp != ves[temp]) {
temp = ves[temp];
}
/*
其实一个普通的并查集中,find函数只要上面这一部分就可以了
之所以我们加上下面的这个while循环,是为了缩小上面的while
循环的循环次数。你仔细阅读下面这个代码就会知道,这个代码的
意义其实就是将一个派别中的所有成员的上级都直接变成这个派别
的老大。
*/
int j,ptr = k;
while (ves[ptr] != temp) {
j = ves[ptr];
ves[ptr] = temp;
ptr = j;
}
return temp;
}
//这个函数是合并操作的,不用我多说你自己也看得明白吧
void join(int a, int b)
{
int x = find(a);
int y = find(b);
if (x != y) ves[x] = y;
}
总结
当你理解了并查集之后,你就会知道这个算法是如此的简单,但它的思路很是巧妙,而且用处确实很实用,是一种应该掌握的算法。