本文主要是介绍《算法》unionfind算法及改进,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
算法详细内容参考《算法》第4版1.5案例研究:union-find算法
分析运行结果:
package union;
import java.util.Arrays;
import java.util.Random;class WeightQuickUnion {private int[] id; //分量id,每个节点所属集合标识private int[] size; //以该节点为根其分量大小private int count; //分量数量//initializepublic WeightQuickUnion(int N){count = N; //初始化每个节点都是一个分量,union时减少countid = new int[N];for(int i = 0; i < N; ++i){id[i] = i;}size = new int[N];for(int i = 0; i < N; ++i){size[i] = 1;}}public int count(){return count;}public boolean connected(int left, int right){return find(left) == find(right);}//O(logN) time 返回节点所属集合标识public int find(int node){while(node != id[node]){node = id[node];}return node;}public void find_quick_union(int left, int right){int left_id = find(left);int right_id = find(right);if(left_id != right_id){if(size[left_id] < size[right_id]){id[left_id] = right_id;size[left_id] += size[right_id];}else{id[right_id] = left_id;size[right_id] += size[left_id];}--count;}return ;}
}class WeightQuickCompressUnion {private int[] id; //分量id,每个节点所属集合标识private int[] size; //以该节点为根其分量大小private int count; //分量数量//initializepublic WeightQuickCompressUnion(int N){count = N; //初始化每个节点都是一个分量,union时减少countid = new int[N];for(int i = 0; i < N; ++i){id[i] = i;}size = new int[N];for(int i = 0; i < N; ++i){size[i] = 1;}}public int count(){return count;}public boolean connected(int left, int right){return find(left) == find(right);}//O(logN) time 返回节点所属集合标识public int find(int node){int root = node;while(root != id[root]){root = id[root];}while(node != root){node = id[node];id[node] = root;}return root;}public void find_quick_union(int left, int right){int left_id = find(left);int right_id = find(right);if(left_id != right_id){if(size[left_id] < size[right_id]){id[left_id] = right_id;size[left_id] += size[right_id];}else{id[right_id] = left_id;size[right_id] += size[left_id];}--count;}return ;}
}final class StdRandom {private static Random random; // pseudo-random number generatorprivate static long seed; // pseudo-random number generator seed// static initializerstatic {// this is how the seed was set in Java 1.4seed = System.currentTimeMillis();random = new Random(seed);}// don't instantiateprivate StdRandom() { }/*** Sets the seed of the pseudorandom number generator.* This method enables you to produce the same sequence of "random"* number for each execution of the program.* Ordinarily, you should call this method at most once per program.** @param s the seed*/public static void setSeed(long s) {seed = s;random = new Random(seed);}/*** Returns the seed of the pseudorandom number generator.** @return the seed*/public static long getSeed() {return seed;}/*** Returns a random real number uniformly in [0, 1).* @return a random real number uniformly in [0, 1)*/public static double uniform() {return random.nextDouble();}/*** Returns a random integer uniformly in [0, n).* * @param n number of possible integers* @return a random integer uniformly between 0 (inclusive) and {@code n} (exclusive)* @throws IllegalArgumentException if {@code n <= 0}*/public static int uniform(int n) {if (n <= 0) throw new IllegalArgumentException("argument must be positive");return random.nextInt(n);}}public class Union {private int[] id; //分量id,每个节点所属集合标识private int count; //分量数量//initializepublic Union(int N){count = N; //初始化每个节点都是一个分量,union时减少countid = new int[N];for(int i = 0; i < N; ++i){id[i] = i;}}public boolean quick_find_connected(int left, int right){return quick_find(left) == quick_find(right);}public boolean find_connected(int left, int right){return find(left) == find(right);}//O(1) timepublic int quick_find(int node){return id[node];}//O(n^2) time to union if union N timespublic void quick_find_union(int left, int right){int left_id = quick_find(left); //O(1) time to findint right_id = quick_find(right);if(left_id != right_id){for(int node = 0; node < id.length; ++node){if(quick_find(node) == left_id){id[node] = right_id;}}--count;}return ;}//O(logN) time 返回节点所属集合标识public int find(int node){while(node != id[node]){node = id[node];}return node;}//O(N*treeHeight) time if union N timespublic void find_lazy_quick_union(int left, int right){int left_id = find(left);int right_id = find(right);if(left_id != right_id){id[left_id] = right_id;--count;}return ;}public static void main(String[] args) {// TODO Auto-generated method stubfinal int E = 90000;final int N = 100000;int[][] eadges = new int [E][2];for(int i = 0; i < E; ++i){eadges[i][0] = StdRandom.uniform(N);eadges[i][1] = StdRandom.uniform(N);}
// System.out.println(Arrays.deepToString(eadges));long startTime;long endTime;System.out.println("Union");Union UF = new Union(N);startTime = System.currentTimeMillis();for(int i = 0; i < E; ++i){UF.quick_find_union(eadges[i][0], eadges[i][1]);}endTime = System.currentTimeMillis();System.out.println("Run Time:"+(endTime-startTime)+"ms");/*for(int p = 0; p < N; ++p){for(int q = 0; q < N; ++q)System.out.print(UF.quick_find_connected(p,q)+" ");}*/
// System.out.println("");System.out.println(UF.count);System.out.println("LazyWeight");Union LazyUF = new Union(N);startTime = System.currentTimeMillis();for(int i = 0; i < E; ++i){LazyUF.find_lazy_quick_union(eadges[i][0], eadges[i][1]);}endTime = System.currentTimeMillis();System.out.println("Run Time:"+(endTime-startTime)+"ms");/*for(int p = 0; p < N; ++p){for(int q = 0; q < N; ++q)System.out.print(LazyUF.find_connected(p,q)+" ");}*/
// System.out.println("");System.out.println(LazyUF.count);System.out.println("Weight");WeightQuickUnion WQUF = new WeightQuickUnion(N);startTime = System.currentTimeMillis();for(int i = 0; i < E; ++i){WQUF.find_quick_union(eadges[i][0], eadges[i][1]);}endTime = System.currentTimeMillis();System.out.println("Run Time:"+(endTime-startTime)+"ms");/*for(int p = 0; p < N; ++p){for(int q = 0; q < N; ++q)System.out.print(WQUF.connected(p, q)+" ");}*/
// System.out.println("");System.out.println(WQUF.count());System.out.println("Weight & Compress");WeightQuickCompressUnion WCUF = new WeightQuickCompressUnion(N);startTime = System.currentTimeMillis();for(int i = 0; i < E; ++i){WCUF.find_quick_union(eadges[i][0], eadges[i][1]);}endTime = System.currentTimeMillis();System.out.println("Run Time:"+(endTime-startTime)+"ms");/*for(int p = 0; p < N; ++p){for(int q = 0; q < N; ++q)System.out.print(WCUF.connected(p, q)+" ");}*/
// System.out.println("");System.out.println(WCUF.count());}
}
打印出来的结果:
Union
Run Time:5248ms
20303
LazyWeight
Run Time:739ms
20303
Weight
Run Time:736ms
20303
Weight & Compress
Run Time:19ms
20303
调大边数E
final int E = 99000;
final int N = 100000;
Union
Run Time:6076ms
16619
LazyWeight
Run Time:1296ms
16619
Weight
Run Time:1271ms
16619
Weight & Compress
Run Time:19ms
16619
调小边数E
final int E = 1000;
final int N = 100000;
Union
Run Time:43ms
99000
LazyWeight
Run Time:0ms
99000
Weight
Run Time:1ms
99000
Weight & Compress
Run Time:0ms
99000
union、LazyWeight、Weight、Weight & Compress对应教材quick-find、quick-union、加权quick-union、路径压缩的加权quick-union算法。
边数比较小时图比较稀疏,输出结果中99000表示一共99000个分量,而总共节点为100000,quick-union中每棵树高度都不大所以运行起来比加权quick-union更快,因为quick-union中多了求树节点数量、将小树指向大树的步骤。而增大边数时加权quick-union效果比quick-union更好,因为加权quick-union可以保证树高度最多为logN,N为节点数。挺有用的结论,把小树的根指向大树的根可以保证树高度小于logN,证明:
小树节点总数N1,高度为h1,大树节点总数N2,高度为h2,;将小树根指向大树根节点会造成小树节点高度加1
h1+1 <= log N1 + 1 = log(N1+N1) <= log(N1 + N2)
大树节点高度不变
所以合并之后的树高度不大于log(N1 + N2)
又单个节点高度为0,数学归纳法即知
这篇关于《算法》unionfind算法及改进的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!