并查集实现-(秩优化+路径压缩+java)
2018-02-09 02:44
393 查看
并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。 并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。 --百度百科
并查集(Union/Find)从名字可以看出,主要涉及两种基本操作:合并和查找。这说明,初始时并查集中的元素是不相交的,经过一系列的基本操作(Union),最终合并成一个大的集合。合并之后的结构逻辑上是一个森林。
应用
并查集数据结构非常简单,基本操作也很简单。用途也简单,就是检查两点是否联通。在实际中有很多应用,比如,求解无向图中连通分量的个数,生成迷宫……这些应用本质都是初始时都是一个个不连通的对象,经过一步步处理,变成连通的了,如迷宫,初始时,起点和终点不连通,随机地打开起点到终点路径上的一个方向,直至起点和终点连通了,就生成了一个迷宫。存储结构
int count;// 元素数量int[] rank; // 下标所对应节点层数
int[] parent;// 下标表示节点,值表示其父元素
主要函数及思路
void Union(int p,int q);合并两个节点的思路就是,先找到两个节点的根节点,然后将一个节点接到另外一个节点下面即可,涉及到一个优化就是使用秩优化,因为如果随意将一个节点接到另外一个节点也是可以的,但是这种方法效率低下且慢,因为有时候一个根节点的层级很高,而另外一个层级很低,如果把层级高的放到层级低的下面这样层级就会增加,查找所用的时间就更长,数据少还好,多的时候效率太低了,所以引入一个数组记录每个根节点层数,每次合并把层数低的放到层数高的节点下面,这样层数就不会增加。
int find(int p);
此函数会返回p元素对应的根节点元素,因为我们初始化的时候根节点的父元素就是自己,所以我们查找的时候只要顺着查上去,遇到一个元素的父节点就是自己的,那么这个就是根节点了,在这里可以有一个优化,因为一旦元素一多起来,可能退化成一条链,每次得到根节点都将会使用O(n)的复杂度,这显然不是我们想要的。对此,我们必须要进行路径压缩,即我们找到最久远的祖先时“顺便”把它的子孙直接连接到它上面。这就是路径压缩了。
boolean isConnected(int p, int q);
只要调用find方法找到各自的根节点,如果p和q的根节点一样,那么就代表联通
具体代码:
import java.util.Random; public class UnionFind { private int count;// 元素数量 private int[] rank; // 层数 private int[] parent;// 节点 UnionFind(int count) { //初始化操作 this.count = count; rank = new int[count]; parent = new int[count]; for (int i = 0; i < count; i++) { rank[i] = 1; parent[i] = i; } } public void Union(int p, int q) { // 合并两个元素 int pRoot=find(p); //定位到其根节点 int qRoot=find(q); if(pRoot==qRoot) //如果两个都相等,证明已经是连接好的,不必再合并 return; if(rank[pRoot]>rank[qRoot]){ //如果p根节点的层数大于q的,那么将qRoot接到pRoot下面,这样接好以后层级不会增加 parent[qRoot]=pRoot; }else if(rank[pRoot]<rank[qRoot]){ parent[pRoot]=qRoot; }else //如果等于,则随便哪边层级都会加一 { parent[pRoot]=qRoot; rank[qRoot]=rank[qRoot]+1; } } public int find(int p) {// 查找元素 返回根元素 while(p!=parent[p]){ parent[p]=parent[parent[p]]; //路径压缩 p=parent[p]; } return p; } public boolean isConnected(int p, int q) { return find(p) == find(q); } public static void main(String[] arg){ int N=10000000; UnionFind uf=new UnionFind(N); double startTime=System.currentTimeMillis(); int tempA; int tempB; Random random = new Random(); //进行N次合并操作 for(int i=0;i<N;i++){ tempA=random.nextInt(N)%N; tempB=random.nextInt(N)%N; uf.Union(tempA, tempB); } //进行N次查找 for(int i=0;i<N;i++){ tempA=random.nextInt(N)%N; tempB=random.nextInt(N)%N; uf.isConnected(tempA, tempB); } double endTime=System.currentTimeMillis(); System.out.println(endTime-startTime); } }
经过本机测试,千万级数据,只需要4025毫秒,相当于4秒,百万级数据217毫秒,0.217秒,当然这个数据根据电脑配置不同会有所不同,但是效率是很明显的,这就是并查集的优势所在,一个算法解决的问题越少所需要的时间应该越少。
相关文章推荐
- 算法代码实现之Union-Find,Java实现,quick-find、quick-union、加权quick-union(附带路径压缩优化)
- Android 利用Java实现压缩与解压缩(zip、gzip)支持中文路径
- java环境 + yuicompressor 实现代码压缩优化
- 并查集的两种优化(按秩合并,路径压缩)
- 加权路径压缩并查集实现
- Android 利用Java实现压缩与解压缩(zip、gzip)支持中文路径
- 利用Java实现压缩与解压缩(zip、gzip)支持中文路径
- 算法代码实现之Union-Find,C++实现,quick-find、quick-union、加权quick-union(附带路径压缩优化)
- 并查集,battles over cities,路径压缩,优化与封装,无向图连通性
- 并查集的两个优化(秩优化+路径压缩)
- 并查集的优化---路径压缩与启发式合并
- 利用Java实现压缩与解压缩(zip、gzip)支持中文路径
- 十、森林与并查集---(6)并查集路径压缩优化
- 数据结构实现之并查集(使用按秩合并和路径压缩)
- 利用Java实现压缩与解压缩(zip、gzip)支持中文路径
- Android 利用Java实现压缩与解压缩(zip、gzip)支持中文路径
- HDU 1856 More is better(并查集路径压缩+剪枝优化+暴力枚举)
- Android 利用Java实现压缩与解压缩(zip、gzip)支持中文路径
- 算法代码实现之Union-Find,Golang(Go语言)实现,quick-find、quick-union、加权quick-union(附带路径压缩优化)
- 利用Java实现压缩与解压缩(zip、gzip)支持中文路径