并查集详解

来源:互联网 发布:php干什么用的 编辑:程序博客网 时间:2024/05/17 10:41

                         并查集  

并查集是一种关于数据结构的基础算法,主要用于高效的判断两个元素是否属于一个集合。在物理意义上用数组实习现,但其逻辑意义上由树结构实现。

 并且在并查集中,如果刚刚开始的时候,A,B 两个集合是相互无关的两个集合,一旦A与B中任意两个元素产生的关系,那么A中的所有元素与B中的所有元素都产生了关系(即都属于了一个集合中)。

 如何判断两个元素是否属于一个集合呢? 我们可以引入一个帮主这样一个概念,对于两个元素如果他们俩的帮主是一个人的话,那么他们就是属于一个集合的,如果他们的帮主不是一个人的就不是一个集合的。

  首先由一个基础的数组给予实现。

  used[i]数值来表示。主要由三个函数组成:初始化函数, 查找函数,合并函数。

 首先初始化函数:

void init (){    for (int i = 0; i < max_n; i ++)        used[i] = i;}

 最开始的时候调用初始化函数,使得每个人的上级都是其自己。

然后是查找函数:

迭代版:

int Find (int a){    return used[a] == a? a : used[a] = Find(used[a]);}

递归版:

int Find(int x){    int p = x;    while( used[p] != p)        p = used[p];    int i = x, j;
    while( i != p)    {        j = used[i];        used[i] = p;        i = j;    }    // 这个循环中蕴涵了一个路径压缩过程,使算法更有效率 进行。    return i;}


查找a的老大是谁。这个是函数的的迭代形式。首先我们要明白在每一个集合中都有一个帮主,什么样的才能称为帮主呢?那就是他的上级是他自己,用代码来表示就是:used[i] == i; 所以我们进行递归判断 如果一个元素的帮主是他自己的话就返回他自己的序号,如果不是他自己的话就要去查找他上级的帮主,以此类推。(每个人都有自己的上级可以直接获取就是 used[i]的值,但是要想找到这个人的帮主就要一直去问自己的上级。)

然后是合并函数:

void mix (int a, int b){    int p1 = Find (a);    int p2 = Find (b);    if (p1 != p2) {        used[p1] = p2;    }}


如何把a所在的帮派和b所在的帮派合并到一起呢?我们先找到a的帮主,然后再找到b的帮主,只要a的帮主认b的帮主为老大,那么a帮所有的人的帮主都会变成b帮的帮主,那么这两个帮派都合并成一个帮派了。



  废话不多说上题:杭电的一道题  HDU-1232.

       畅通工程

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

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

Output
对每个测试用例,在1行里输出最少还需要建设的道路数目。 



Sample Input
4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0


Sample Output
1
0
2
998

   题目大意:有N个城市(这些城市从1到N进行编号),城市之间有M条道路,每条道路将两个城市连接到一起,在输入城市个数N与道路数目M后,输入每条道路将两个城市的连接情况。然后要求输出 最少还需要建设多少条道路使所有的城市连接到一起(即任意两个城市实现互通)。

  这个题目实际上是求一个图中连通分支的个数,因为需新建设的道路数就是连通分支的个数减1。这个题目就需要用到并查集算法。

  最开始的时候每个城市都是属于一个独立的连通分支,每当进行合并的时候,连通分支的数目就减1,所以先设置一个变量w其初始值为N-1, 在每次合并操作中都对其进行自减操作,最后输出w即可。

 直接上代码:

#include <iostream>#include <stdio.h>using namespace std;#define max_n 1010int used[max_n];int m, n;int x, y;int w;int Find(int x){    int p = x;    while( used[p] != p)        p = used[p];    int i = x, j;    while( i != p)    {        j = used[i];        used[i] = p;        i = j;    }    return i;}void mix(int f1, int f2){    int p1 = Find(f1);    int p2 = Find(f2);    if( p1 != p2)    {        used[p1] = p2;        w--;    }}int main(){    while(scanf("%d%d", &n, &m )&&n)    {        w = n - 1;        for( int i = 0; i < n+1; i ++)            used[i] = i;        for( int i = 0; i < m; i++)        {            scanf("%d %d",&x, &y);            mix(x,y);        }        printf("%d\n",w);    }    return 0;}


  

1 0
原创粉丝点击