并查集

来源:互联网 发布:阿里云销售工资 编辑:程序博客网 时间:2024/06/18 03:32

并查集

定义

并查集(Union-find Sets)是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题。一些常见的用途有求连通子图、求最小生成树的 Kruskal 算法和求最近公共祖先(Least Common Ancestors, LCA)等。

  • 存放数据的几何关系,如{1,2} {3,4} {5}
  • 支持操作
    • 建立新集合
    • 查找某个元素属于哪个集合
    • 合并两个集合
  • 均摊时间复杂度近似O(1)

这里写图片描述

假设我们输入了一组整数对,即上图中的(4, 3) (3, 8)等等,每对整数代表这两个points/sites是连通的。那么随着数据的不断输入,整个图的连通性也会发生变化,从上图中可以很清晰的发现这一点。同时,对于已经处于连通状态的points/sites,直接忽略,比如上图中的(8, 9)。

并查集用树来表示,则树的每个节点就表示集合中的一个元素,树根对应的元素就是该集合的代表(如数组中游标为4的对应元素也为4),如下图:
这里写图片描述
注:根节点总能指向自己

应用

  • 网络连接判断:
    如果每个pair中的两个整数分别代表一个网络节点,那么该pair就是用来表示这两个节点是需要连通的。那么为所有的pairs建立了动态连通图后,就能够尽可能少的减少布线的需要,因为已经连通的两个节点会被直接忽略掉。

  • 变量名等同性(类似于指针的概念):
    在程序中,可以声明多个引用来指向同一对象,这个时候就可以通过为程序中声明的引用和实际对象建立动态连通图来判断哪些引用实际上是指向同一对象。

并查集参考 http://blog.csdn.net/dm_vincent/article/details/7655764

实现

1. 创建一个并查集(每个元素都能表示一个集合)

这里写图片描述
相当于每个元素都指向自己,对应的代码也只是创建数组。
用树来表示为:
这里写图片描述

数值表示此元素属于哪个集合

const int MAXSIZE = 500;int uset[MAXSIZE];void makeSet(int size) {    for(int i = 0;i < size;i++) uset[i] = i;}

2. 集合的查找

此时的查找很容易,因为是一一对应的。
到下一步后有了集合的合并,就会看到递归的作用了

int find(int x) {    if (x != uset[x]) uset[x] = find(uset[x]);    return uset[x];}

3.集合的合并

例如:将下标为1对应的元素设为4,那么就相当于把下标为1对应的集合并入到集合4。
对应的代码 :uset[1]=4 ,这样就将集合1并入集合4
这里写图片描述

void unionSet(int x, int y) {    if ((x = find(x)) == (y = find(y))) return;    if (rank[x] > rank[y]) uset[y] = x;    else {        uset[x] = y;        if (rank[x] == rank[y]) rank[y]++;    }}

这里也应用一个简单的启发式策略——按秩合并。该方法使用秩来表示树高度的上界,在合并时,总是将具有较小秩的树根指向具有较大秩的树根。简单的说,就是总是将比较矮的树作为子树,添加到较高的树中。为了保存秩,需要额外使用一个与 uset 同长度的数组,并将所有元素都初始化为 0。

此时再看查找下标为1的元素属于哪个集合时,就会发现递归的作用。

参考:https://www.cnblogs.com/cyjb/p/UnionFindSets.html