/**
* 并查集
*/publicinterfaceUnionFind{/**
* 所有节点数量
* @return
*/intcount();/**
* 将a和b合并
* @param a
* @param b
*/voidunion(int a,int b);/**
* 查找a属于哪个集合
* @param a
* @return
*/intfind(int a);/**
* 查找a和b是否同属一个集合
* @param a
* @param b
* @return
*/booleanisConnected(int a,int b);}
值直接为所属集合id的并查集
publicclassUnionFindV1implementsUnionFind{privateint[] id;publicUnionFindV1(int capacity){assert capacity >0;this.id =newint[capacity];for(int i =0; i <this.id.length; i++){this.id[i]= i;}}@Overridepublicintcount(){returnthis.id.length;}@Overridepublicvoidunion(int a,int b){assert a >=0&& a < id.length;assert b >=0&& b < id.length;int aRoot =find(a);int bRoot =find(b);if(aRoot == bRoot){return;}// 将所有root为a的节点root改为bfor(int i =0; i < id.length; i++){if(id[i]== aRoot){
id[i]= bRoot;}}}@Overridepublicintfind(int a){returnthis.id[a];}@OverridepublicbooleanisConnected(int a,int b){returnfind(a)==find(b);}@Overridepublic String toString(){
StringBuilder sb =newStringBuilder();
sb.append("index:\t");for(int i =0; i < id.length; i++){
sb.append(i).append('\t');}
sb.append("\nroot:\t");for(int i : id){
sb.append(i).append('\t');}return sb.toString();}}
有关联的并查集
publicclassUnionFindV2implementsUnionFind{privateint[] parent;publicUnionFindV2(int capacity){this.parent =newint[capacity];for(int i =0; i <this.parent.length; i++){this.parent[i]= i;}}@Overridepublicintcount(){returnthis.parent.length;}@Overridepublicvoidunion(int a,int b){assert a >=0&& a <this.parent.length;assert b >=0&& b <this.parent.length;int aRoot =find(a);int bRoot =find(b);if(aRoot == bRoot){return;}this.parent[aRoot]= bRoot;}@Overridepublicintfind(int a){assert a >=0&& a <this.parent.length;while(a !=this.parent[a]){
a =this.parent[a];}return a;}@OverridepublicbooleanisConnected(int a,int b){returnfind(a)==find(b);}@Overridepublic String toString(){
StringBuilder sb =newStringBuilder();
sb.append("index:\t");for(int i =0; i <this.parent.length; i++){
sb.append(i).append('\t');}
sb.append("\nroot:\t");for(int i :this.parent){
sb.append(find(i)).append('\t');}return sb.toString();}}
基于size的优化
import java.util.Arrays;publicclassUnionFindBaseSizeimplementsUnionFind{privateint[] parent;privateint[] unionSize;publicUnionFindBaseSize(int capacity){this.parent =newint[capacity];for(int i =0; i <this.parent.length; i++){this.parent[i]= i;}this.unionSize =newint[capacity];
Arrays.fill(this.unionSize,1);}@Overridepublicintcount(){returnthis.parent.length;}@Overridepublicvoidunion(int a,int b){assert a >=0&& a <this.parent.length;assert b >=0&& b <this.parent.length;int aRoot =find(a);int bRoot =find(b);if(aRoot == bRoot){return;}// 少的挂到多的上if(this.unionSize[aRoot]<this.unionSize[bRoot]){this.parent[aRoot]= bRoot;this.unionSize[bRoot]+=this.unionSize[aRoot];}else{this.parent[bRoot]= aRoot;this.unionSize[aRoot]+=this.unionSize[bRoot];}}@Overridepublicintfind(int a){assert a >=0&& a <this.parent.length;while(a !=this.parent[a]){
a =this.parent[a];}return a;}@OverridepublicbooleanisConnected(int a,int b){returnfind(a)==find(b);}@Overridepublic String toString(){
StringBuilder sb =newStringBuilder();
sb.append("index:\t");for(int i =0; i <this.parent.length; i++){
sb.append(i).append('\t');}
sb.append("\nroot:\t");for(int i :this.parent){
sb.append(find(i)).append('\t');}return sb.toString();}}
基于rank的优化
import java.util.Arrays;publicclassUnionFindBaseRankimplementsUnionFind{privateint[] parent;privateint[] rank;publicUnionFindBaseRank(int capacity){this.parent =newint[capacity];for(int i =0; i <this.parent.length; i++){this.parent[i]= i;}this.rank =newint[capacity];
Arrays.fill(this.rank,1);}@Overridepublicintcount(){returnthis.parent.length;}@Overridepublicvoidunion(int a,int b){assert a >=0&& a <this.parent.length;assert b >=0&& b <this.parent.length;int aRoot =find(a);int bRoot =find(b);if(aRoot == bRoot){return;}// 层数少的挂层数多的if(this.rank[aRoot]<this.rank[bRoot]){this.parent[aRoot]= bRoot;}elseif(this.rank[aRoot]>this.rank[bRoot]){this.parent[bRoot]= aRoot;}else{// 层数相等时,随意挂,但被挂上的根要rank++this.parent[bRoot]= aRoot;this.rank[aRoot]+=1;}}@Overridepublicintfind(int a){assert a >=0&& a <this.parent.length;while(a !=this.parent[a]){
a =this.parent[a];}return a;}@OverridepublicbooleanisConnected(int a,int b){returnfind(a)==find(b);}@Overridepublic String toString(){
StringBuilder sb =newStringBuilder();
sb.append("index:\t");for(int i =0; i <this.parent.length; i++){
sb.append(i).append('\t');}
sb.append("\nroot:\t");for(int i :this.parent){
sb.append(find(i)).append('\t');}return sb.toString();}}
基于size优化+路径压缩
两步一压缩
import java.util.Arrays;/**
* 基于size优化后再做路径压缩
*/publicclassUnionFindBaseSizePathCompressionV1implementsUnionFind{privateint[] parent;privateint[] unionSize;publicUnionFindBaseSizePathCompressionV1(int capacity){this.parent =newint[capacity];for(int i =0; i <this.parent.length; i++){this.parent[i]= i;}this.unionSize =newint[capacity];
Arrays.fill(this.unionSize,1);}@Overridepublicintcount(){returnthis.parent.length;}@Overridepublicvoidunion(int a,int b){assert a >=0&& a <this.parent.length;assert b >=0&& b <this.parent.length;int aRoot =find(a);int bRoot =find(b);if(aRoot == bRoot){return;}// 少的挂到多的上if(this.unionSize[aRoot]<this.unionSize[bRoot]){this.parent[aRoot]= bRoot;this.unionSize[bRoot]+=this.unionSize[aRoot];}else{this.parent[bRoot]= aRoot;this.unionSize[aRoot]+=this.unionSize[bRoot];}}@Overridepublicintfind(int a){assert a >=0&& a <this.parent.length;while(a !=this.parent[a]){this.parent[a]=this.parent[this.parent[a]];
a =this.parent[a];}return a;}@OverridepublicbooleanisConnected(int a,int b){returnfind(a)==find(b);}@Overridepublic String toString(){
StringBuilder sb =newStringBuilder();
sb.append("index:\t");for(int i =0; i <this.parent.length; i++){
sb.append(i).append('\t');}
sb.append("\nroot:\t");for(int i :this.parent){
sb.append(find(i)).append('\t');}return sb.toString();}}
直接压缩到根
import java.util.Arrays;/**
* 基于size优化后再做路径压缩
*/publicclassUnionFindBaseSizePathCompressionV2implementsUnionFind{privateint[] parent;privateint[] unionSize;publicUnionFindBaseSizePathCompressionV2(int capacity){this.parent =newint[capacity];for(int i =0; i <this.parent.length; i++){this.parent[i]= i;}this.unionSize =newint[capacity];
Arrays.fill(this.unionSize,1);}@Overridepublicintcount(){returnthis.parent.length;}@Overridepublicvoidunion(int a,int b){assert a >=0&& a <this.parent.length;assert b >=0&& b <this.parent.length;int aRoot =find(a);int bRoot =find(b);if(aRoot == bRoot){return;}// 少的挂到多的上if(this.unionSize[aRoot]<this.unionSize[bRoot]){this.parent[aRoot]= bRoot;this.unionSize[bRoot]+=this.unionSize[aRoot];}else{this.parent[bRoot]= aRoot;this.unionSize[aRoot]+=this.unionSize[bRoot];}}@Overridepublicintfind(int a){assert a >=0&& a <this.parent.length;if(a !=this.parent[a]){this.parent[a]=find(this.parent[a]);}return a;}@OverridepublicbooleanisConnected(int a,int b){returnfind(a)==find(b);}@Overridepublic String toString(){
StringBuilder sb =newStringBuilder();
sb.append("index:\t");for(int i =0; i <this.parent.length; i++){
sb.append(i).append('\t');}
sb.append("\nroot:\t");for(int i :this.parent){
sb.append(find(i)).append('\t');}return sb.toString();}}
基于rank优化+路径压缩
两步一压缩
import java.util.Arrays;/**
* 基于rank优化后再做路径压缩
*/publicclassUnionFindBaseRankPathCompressionV1implementsUnionFind{privateint[] parent;privateint[] unionSize;publicUnionFindBaseRankPathCompressionV1(int capacity){this.parent =newint[capacity];for(int i =0; i <this.parent.length; i++){this.parent[i]= i;}this.unionSize =newint[capacity];
Arrays.fill(this.unionSize,1);}@Overridepublicintcount(){returnthis.parent.length;}@Overridepublicvoidunion(int a,int b){assert a >=0&& a <this.parent.length;assert b >=0&& b <this.parent.length;int aRoot =find(a);int bRoot =find(b);if(aRoot == bRoot){return;}// 少的挂到多的上if(this.unionSize[aRoot]<this.unionSize[bRoot]){this.parent[aRoot]= bRoot;this.unionSize[bRoot]+=this.unionSize[aRoot];}else{this.parent[bRoot]= aRoot;this.unionSize[aRoot]+=this.unionSize[bRoot];}}@Overridepublicintfind(int a){assert a >=0&& a <this.parent.length;while(a !=this.parent[a]){this.parent[a]=this.parent[this.parent[a]];
a =this.parent[a];}return a;}@OverridepublicbooleanisConnected(int a,int b){returnfind(a)==find(b);}@Overridepublic String toString(){
StringBuilder sb =newStringBuilder();
sb.append("index:\t");for(int i =0; i <this.parent.length; i++){
sb.append(i).append('\t');}
sb.append("\nroot:\t");for(int i :this.parent){
sb.append(find(i)).append('\t');}return sb.toString();}}
直接压缩到根
import java.util.Arrays;/**
* 基于rank优化后再做路径压缩
*/publicclassUnionFindBaseRankPathCompressionV2implementsUnionFind{privateint[] parent;privateint[] unionSize;publicUnionFindBaseRankPathCompressionV2(int capacity){this.parent =newint[capacity];for(int i =0; i <this.parent.length; i++){this.parent[i]= i;}this.unionSize =newint[capacity];
Arrays.fill(this.unionSize,1);}@Overridepublicintcount(){returnthis.parent.length;}@Overridepublicvoidunion(int a,int b){assert a >=0&& a <this.parent.length;assert b >=0&& b <this.parent.length;int aRoot =find(a);int bRoot =find(b);if(aRoot == bRoot){return;}// 少的挂到多的上if(this.unionSize[aRoot]<this.unionSize[bRoot]){this.parent[aRoot]= bRoot;this.unionSize[bRoot]+=this.unionSize[aRoot];}else{this.parent[bRoot]= aRoot;this.unionSize[aRoot]+=this.unionSize[bRoot];}}@Overridepublicintfind(int a){assert a >=0&& a <this.parent.length;if(a !=this.parent[a]){this.parent[a]=find(this.parent[a]);}return a;}@OverridepublicbooleanisConnected(int a,int b){returnfind(a)==find(b);}@Overridepublic String toString(){
StringBuilder sb =newStringBuilder();
sb.append("index:\t");for(int i =0; i <this.parent.length; i++){
sb.append(i).append('\t');}
sb.append("\nroot:\t");for(int i :this.parent){
sb.append(find(i)).append('\t');}return sb.toString();}}
性能测试
import java.lang.reflect.Constructor;import java.util.Random;import org.junit.Test;publicclassTestUF{@TestpublicvoidunionFind(){int capacity =10000000;// unionFindTest(UnionFindV1.class, capacity);// unionFindTest(UnionFindV2.class, capacity);unionFindTest(UnionFindBaseSize.class, capacity);unionFindTest(UnionFindBaseSizePathCompressionV1.class, capacity);unionFindTest(UnionFindBaseSizePathCompressionV2.class, capacity);unionFindTest(UnionFindBaseRank.class, capacity);unionFindTest(UnionFindBaseRankPathCompressionV1.class, capacity);unionFindTest(UnionFindBaseRankPathCompressionV2.class, capacity);}privatevoidunionFindTest(Class<?extendsUnionFind> clazz,int capacity){try{
Random random =newRandom(37);
Constructor<?extendsUnionFind> constructor = clazz.getConstructor(int.class);
UnionFind unionFind = constructor.newInstance(capacity);long start = System.currentTimeMillis();for(int i =0; i < capacity; i++){int a = random.nextInt(capacity);int b = random.nextInt(capacity);
unionFind.union(a, b);}for(int i =0; i < capacity; i++){int a = random.nextInt(capacity);int b = random.nextInt(capacity);
unionFind.isConnected(a, b);}
System.out.println(clazz.getSimpleName()+" , capacity is "+ capacity +" time is "+(System.currentTimeMillis()- start)/1000.0+"s");}catch(Exception e){
e.printStackTrace();}}}