并查集可以称之为不相交集合,在处理查找几个元素是否在同一集合时使用并查集可以达到非常快的处理速度,并查集的思想有一点很重要,就是利用树的思想,表示不同的不相交的集合状态,利用根节点作为代表元素,来对整个集合的数据进行处理。
并查集是一种非常精巧而实用的数据结构,主要用于处理一些不相交集合的合并问题,一些常见的用途有求连通子图、最小生成树的karkal算法、和求最近共同祖先等问题。
使用并查集时,首先会初始化一组不相交的动态集合,一般用其数组对应的下标表示集合中的一个元素。每个集合可能包含一个或几个元素,选出其中的某个元素作为代表。
对每个集合中具体包含了那些元素是不关心的,具体选那个元素作为代表也是不关心的,我们关心的是,对于一个元素,可以很快的找到这个元素所在集合的代表,以及合并两个元素所在的集合。
并查集的基本操作 :
init()初始化操作,构建一群根作为自己的森林。
Getf()查找操作,将getf()函数返回对应集合的根节点的编号。
Merge()合并操作,将两个元素对应的集合进行合并,如果是同一集合,则不进行操作,否则对两颗树进行合并。
然后就是判断根节点是否相同的在操作。
最后是记录根节点个数的操作,也就是不同的集合个数。
总体思路 :
我们把每一个节点所在树的根节点存入f【】数组中。
输入两个数查找他们的根节点。
对他们所在的根节点进行合并。
最后输入要查找的两个节点,找到他们的根节点,判断他们的根节点是否相同。
来看一个实例
首先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些对城镇之间是有道路直接相连的。最后要解决的是整幅图的连通性问题。比如随意给你两个点,让你判断它们是否连通,或者问你整幅图一共有几个连通分支,也就是被分成了几个互相独立的块。像畅通工程这题,问还需要修几条路,实质就是求有几个连通分支。如果是1个连通分支,说明整幅图上的点都连起来了,不用再修路了;如果是2个连通分支,则只要再修1条路,从两个分支中各选一个点,把它们连起来,那么所有的点都是连起来的了;如果是3个连通分支,则只要再修两条路……
以下面这组数据输入数据来说明
4 2
1 3
4 3
第一行告诉你,一共有4个点,2条路。下面两行告诉你,1、3之间有条路,4、3之间有条路。那么整幅图就被分成了1-3-4和2两部分。只要再加一条路,把2和其他任意一个点连起来,畅通工程就实现了,那么这个这组数据的输出结果就是1。好了,现在编程实现这个功能吧,城镇有几百个,路有不知道多少条,而且可能有回路。 这可如何是好?
我以前也不会呀,自从用了并查集之后,嗨,效果还真好!我们全家都用它!
并查集由一个整数型的数组和两个函数构成。数组pre[]记录了每个点的前导点是什么,函数find是查找,join是合并。
int pre[1000 ];
int find(int x) //查找根节点
{
int r=x;
while ( pre[r ] != r ) //返回根节点 r
r=pre[r ];
int i=x , j ;
while( i != r ) //路径压缩
{
j = pre[ i ]; // 在改变上级之前用临时变量 j 记录下他的值
pre[ i ]= r ; //把上级改为根节点
i=j;
}
return r ;
}
void join(int x,int y) //判断x y是否连通,
//如果已经连通,就不用管了 //如果不连通,就把它们所在的连通分支合并起,
{int fx=find(x),fy=find(y);
if(fx!=fy)
pre[fx ]=fy;
}