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 }
輸出: