图-最小生成树算法之Kruskal及其Java实现
1.Kruskal算法
Kruskal算法基于贪心,因此它追求的是近似最优解,也就是说由Kruskal得出的生成树并不一定是最优解。
Kruskal算法求最小生成树的关键在于,每次选取图中权值最小(及贪心),并不会构成环的边,直到所有点都被囊括。一般,边的个数=点的个数-1。
如下无向图:
要找到最小生成树,克鲁斯卡尔算法的步骤如下:
2.Java实现
针对上述《算法导论》中的例子,有Java代码如下:
1 import java.util.ArrayList; 2 import java.util.Collections; 3 import java.util.Iterator; 4 5 //图类 无向图 6 class G{ 7 ArrayList<V> vs=new ArrayList<V>(); 8 ArrayList<E> es=new ArrayList<E>(); 9 10 public void addV(V v) { 11 vs.add(v); 12 } 13 14 public void add(E e) { 15 es.add(e); 16 } 17 } 18 19 //点 20 class V{ 21 String name; 22 boolean isvisited=false; 23 24 public V(String name) { 25 this.name=name; 26 } 27 28 @Override 29 public String toString() { 30 return name; 31 } 32 } 33 34 //边 35 class E{ 36 V v1; //点 37 V v2; //点 38 int Weight; //权重 39 boolean isvisited=false; 40 41 public E(V v1,V v2,int Weight) { 42 this.v1=v1; 43 this.v2=v2; 44 this.Weight=Weight; 45 } 46 47 @Override 48 public String toString() { 49 return "(" + v1 + ", " + v2 + ":" + Weight + ")"; 50 } 51 } 52 53 public class MinTree { 54 //克鲁斯卡尔 55 static void kruskal(G graph) { 56 ArrayList<E> edges=graph.es; //存储图的边集合 57 ArrayList<E> forest=new ArrayList<E>(); //存放符合结果的边 58 59 for(E e:edges) { //遍历边集合,在初始化图的时候,边已按照权值排序 60 ArrayList<E> testForest=refreshForest(forest); 61 getEnd(testForest,e.v1,e.v2); 62 if(endV) { //判断是否形成回路 63 System.out.print(e); //输出符合条件(不形成回路,权值最小)的边 64 forest.add(e); 65 } 66 } 67 } 68 69 //将图的边集合按权值排序 70 static ArrayList<E> sortEdgeByWeight(ArrayList<E> es){ 71 for(int i=0;i<es.size();i++) { 72 for(int j=0;j<es.size();j++) { 73 if(es.get(i).Weight<es.get(j).Weight) { 74 Collections.swap(es, i, j); 75 } 76 } 77 } 78 return es; 79 } 80 81 static boolean endV=true; 82 //判断新的边是否会和已有森林形成环:即得到目标点的末节点,由此判断两者是否相同,若相同则有环 83 static void getEnd(ArrayList<E> testForest,V start,V end) { 84 for(E e:testForest) { 85 if(e.isvisited==false) { 86 if(e.v1.equals(start)) { 87 e.isvisited=true; 88 if(e.v2.equals(end)) { 89 endV=false; 90 }else { 91 getEnd(testForest,e.v2,end); 92 } 93 }else if (e.v2.equals(start)) { 94 e.isvisited=true; 95 if(e.v1.equals(end)) { 96 endV=false; 97 }else { 98 getEnd(testForest,e.v1,end); 99 } 100 } 101 } 102 } 103 } 104 105 //刷新森林:将森林中所有边标为未被查看,将endV标志也初始化一下 106 static ArrayList<E> refreshForest(ArrayList<E> forest) { 107 endV=true; 108 for(E e:forest) { 109 e.isvisited=false; 110 } 111 return forest; 112 } 113 114 public static void main(String[] args) { 115 // TODO Auto-generated method stub 116 //创建点 117 V a=new V("a"); 118 V b=new V("b"); 119 V c=new V("c"); 120 V d=new V("d"); 121 V e=new V("e"); 122 V f=new V("f"); 123 V g=new V("g"); 124 V h=new V("h"); 125 V i=new V("i"); 126 //创建点 127 128 //创建边 129 E e0=new E(a,b,4); 130 E e1=new E(a,h,8); 131 E e2=new E(b,h,11); 132 E e3=new E(b,c,8); 133 E e4=new E(h,i,7); 134 E e5=new E(h,g,1); 135 E e6=new E(i,c,2); 136 E e7=new E(i,g,6); 137 E e8=new E(c,d,7); 138 E e9=new E(c,f,4); 139 E e10=new E(g,f,2); 140 E e11=new E(d,f,14); 141 E e12=new E(d,e,9); 142 E e13=new E(f,e,10); 143 //创建边 144 145 //创建图 146 G graph=new G(); 147 graph.addV(a); 148 graph.addV(b); 149 graph.addV(c); 150 graph.addV(d); 151 graph.addV(e); 152 graph.addV(f); 153 graph.addV(g); 154 graph.addV(h); 155 graph.addV(i); 156 ArrayList<E> es=new ArrayList<E>(); 157 es.add(e0); 158 es.add(e1); 159 es.add(e2); 160 es.add(e3); 161 es.add(e4); 162 es.add(e5); 163 es.add(e6); 164 es.add(e7); 165 es.add(e8); 166 es.add(e9); 167 es.add(e10); 168 es.add(e11); 169 es.add(e12); 170 es.add(e13); 171 graph.es=sortEdgeByWeight(es); 172 //创建图 173 174 //输出图 175 ArrayList<V> vertexs=graph.vs; 176 ArrayList<E> edges=graph.es; 177 Iterator iVertex=vertexs.iterator(); 178 Iterator iEdge=edges.iterator(); 179 System.out.println("点集合:"); 180 while(iVertex.hasNext()) { 181 System.out.print(iVertex.next()); 182 } 183 System.out.println(); 184 System.out.println("边集合:"); 185 while(iEdge.hasNext()) { 186 System.out.print(iEdge.next()); 187 } 188 //输出图 189 190 //最小生成树 191 //克鲁斯卡尔 192 System.out.println(""); 193 System.out.println("克鲁斯卡尔:"); 194 kruskal(graph); 195 //最小生成树 196 197 } 198 199 }
输出: