Preface:
Borůvka's algorithm 用在圖形演算法中來找出 MST (minimum spanning tree), 再使用這個演算法有個要求是所有 edge 上的 權重必須是 distinct. 底下是這個演算法的典故:
Pseudocode:
底下為此演算法的 Pseudo code:
簡單說明如下:
Complexity:
Borůvka's algorithm can be shown to take O(log V) iterations of the outer loop until it terminates, and therefore to run in time O(E log V), where E is the number of edges, and V is the number of vertices in G. In planar graphs, and more generally in families of graphs closed under graph minor operations, it can be made to run in linear time, by removing all but the cheapest edge between each pair of components after each stage of the algorithm.
Example:
1. 上面第一列為初始狀態, 即將每個 Vertex 視為一個 Component.
2. 上面第二列為第一次 Iteration, 從每個 Component 找出權重最低的 edge 並加到該 Component. 最後合併完剩下兩個 Component: {ABDF} 與 {CEG}.
3. 上面第三列為第二次 Iteration, 即從 {ABDF} 與 {CEG} 中找出權重最低的 edge, 即 E(B,E)=10. 並合併兩個 Component 後只剩下一個 Component {ABCDEFG}.
Implementation:
根據上面演算法, 下面使用 Java 進行實作:
- Vertex.java : Graph 中的 Vertex 類別
- package map;
- import java.util.Set;
- public class Vertex implements Comparable
{ - public String name;
- public Set
edgeSet= new TreeSet(); - public Vertex(String n){this.name = n;}
- public void addEdge(Vertex to, int cost)
- {
- addEdge(to, cost, false);
- }
- public void addEdge(Vertex to, int cost, boolean isDir)
- {
- edgeSet.add(new Edge(isDir, this, to, cost));
- }
- @Override
- public boolean equals(Object o)
- {
- if(o instanceof Vertex)
- {
- Vertex v = (Vertex)o;
- return this.name.equals(v.name);
- }
- return false;
- }
- @Override
- public int compareTo(Vertex o) {
- return name.compareTo(o.name);
- }
- }
- package map;
- public class Edge implements Comparable
{ - public boolean isDirect = false;
- public Vertex from;
- public Vertex to;
- public int cost;
- public Edge(Vertex f, Vertex t, int cost){this.from = f; this.to = t;this.cost=cost;}
- public Edge(boolean isDir, Vertex f, Vertex t, int cost){this(f,t,cost); isDirect=isDir;}
- @Override
- public boolean equals(Object o)
- {
- if(o instanceof Edge)
- {
- Edge e = (Edge)o;
- if(isDirect)
- {
- return from.equals(e.from) && to.equals(e.to);
- }
- else
- {
- return from.equals(e.from) && to.equals(e.to) ||
- from.equals(e.to) && to.equals(e.from);
- }
- }
- return false;
- }
- @Override
- public int hashCode()
- {
- return from.hashCode()+to.hashCode()+cost;
- }
- @Override
- public String toString()
- {
- if(isDirect) return String.format("%s->%s (%d)", from.name, to.name, cost);
- else return String.format("%s-%s (%d)", from.name, to.name, cost);
- }
- @Override
- public int compareTo(Edge o) {
- int i = from.name.compareTo(o.from.name);
- if(i==0)
- {
- return to.name.compareTo(o.to.name);
- }
- return i;
- }
- }
- package map;
- import java.util.HashMap;
- public class Component {
- public String name="";
- public HashMap
vertexMap = new HashMap(); - public Set
edgeToProcess = new TreeSet(); - public Component(Vertex v){addVertex(v);}
- public void addVertex(Vertex v)
- {
- vertexMap.put(v.name, new Vertex(v.name));
- edgeToProcess.addAll(v.edgeSet);
- name = name+v.name;
- }
- @Override
- public boolean equals(Object o)
- {
- if(o instanceof Component)
- {
- Component c = (Component)o;
- return name.equals(c.name);
- }
- return false;
- }
- public boolean intersect(Set
set){ - for(Vertex v:set) if(vertexMap.containsKey(v.name)) return true;
- return false;
- }
- public void connect(Edge e, Component oc)
- {
- if(e.isDirect)
- {
- vertexMap.get(e.from.name).addEdge(oc.vertexMap.get(e.to.name), e.cost);
- }
- else
- {
- vertexMap.get(e.from.name).addEdge(oc.vertexMap.get(e.to.name), e.cost);
- oc.vertexMap.get(e.to.name).addEdge(vertexMap.get(e.from.name), e.cost);
- }
- vertexMap.putAll(oc.vertexMap);
- edgeToProcess.addAll(oc.edgeToProcess);
- Iterator
iter = edgeToProcess.iterator(); - while(iter.hasNext())
- {
- if(vertexMap.containsKey(iter.next().to.name)) iter.remove();
- }
- StringBuffer nameBuf = new StringBuffer();
- for(Vertex v:vertexMap.values()) nameBuf.append(v.name);
- name = nameBuf.toString();
- }
- @Override
- public String toString(){
- StringBuffer outBuf = new StringBuffer();
- for(Vertex from:vertexMap.values())
- {
- outBuf.append(String.format("Vertex-%s:\n", from.name));
- for(Edge e:from.edgeSet)
- {
- outBuf.append(String.format("\t%s\n", e));
- }
- }
- return outBuf.toString();
- }
- }
- package map;
- import java.util.ArrayList;
- public class Graph {
- public boolean isDirect=false;
- public TreeMap
vtxMap = new TreeMap(); - public void addVertex(int name){addVertex(String.valueOf(name));}
- public void addVertex(String name)
- {
- vtxMap.put(name, new Vertex(name));
- }
- public boolean addEdge(int from, int to, int cost){return addEdge(String.valueOf(from),
- String.valueOf(to),
- cost);}
- public boolean addEdge(String from, String to, int cost)
- {
- Vertex fv = vtxMap.get(from);
- Vertex tv = vtxMap.get(to);
- if(fv!=null && tv!=null)
- {
- if(isDirect)
- {
- fv.addEdge(tv, cost, isDirect);
- }
- else
- {
- fv.addEdge(tv, cost, isDirect);
- tv.addEdge(fv, cost, isDirect);
- }
- return true;
- }
- return false;
- }
- /**
- * BD: Get Minimum Spanning Tree based on Kruskal's algorithm.
- * Ref:
- * - wiki of Kruskal's algorithm:
- * http://en.wikipedia.org/wiki/Bor%C5%AFvka's_algorithm
- * @return
- */
- public Component getMST()
- {
- List
compList = new ArrayList (); - Set
compToAdd = new HashSet (); - Set
apSet = new HashSet (); // Keep already connected vertex - // in each iteration.
- HashMap
vcMap = new HashMap (); - for(Vertex v:vtxMap.values()) {
- Component c = new Component(v);
- compList.add(c);
- vcMap.put(v, c);
- }
- int iterCnt = 1;
- while(compList.size()>1)
- {
- System.out.printf("\t[Info] Iter%d (%d)...\n", iterCnt++, compList.size());
- compToAdd.clear();
- apSet.clear();
- Object[] compArray = compList.toArray();
- for(int i=0; i
- {
- Component c = (Component)compArray[i];
- if(c.intersect(apSet)) continue;
- Edge te = null; int mc=Integer.MAX_VALUE;
- for(Edge e:c.edgeToProcess)
- {
- if(e.cost
- {
- mc = e.cost;
- te = e;
- }
- }
- Component tc = vcMap.get(te.to);
- if(tc!=c)
- {
- System.out.printf("\t[Info] Merge %s with %s (%s)...", c.name, tc.name, te);
- c.connect(te, tc);
- System.out.printf("%s\n", c.name);
- for(Vertex v:c.vertexMap.values())
- {
- vcMap.put(vtxMap.get(v.name), c);
- apSet.add(v);
- }
- //compToAdd.add(c);
- compList.remove(tc);
- }
- }
- /*compList.clear();
- for(Component c:vcMap.values()) compToAdd.add(c);
- compList.addAll(compToAdd);*/
- }
- return compList.get(0);
- }
- @Override
- public String toString()
- {
- StringBuffer outBuf = new StringBuffer();
- for(Vertex from:vtxMap.values())
- {
- outBuf.append(String.format("Vertex-%s:\n", from.name));
- for(Edge e:from.edgeSet)
- {
- outBuf.append(String.format("\t%s\n", e));
- }
- }
- return outBuf.toString();
- }
- public static void main(String args[])
- {
- Graph g = new Graph();
- g.addVertex("A"); g.addVertex("B"); g.addVertex("C"); g.addVertex("D");
- g.addVertex("E"); g.addVertex("F"); g.addVertex("G");
- g.addEdge("A", "B", 7);
- g.addEdge("A", "D", 4);
- g.addEdge("B", "D", 9);
- g.addEdge("B", "E", 10);
- g.addEdge("B", "C", 11);
- g.addEdge("C", "E", 5);
- g.addEdge("D", "E", 15);
- g.addEdge("D", "F", 6);
- g.addEdge("E", "F", 12);
- g.addEdge("E", "G", 8);
- g.addEdge("F", "G", 13);
- System.out.printf("\t[Info] Graph:\n%s\n", g);
- Component c = g.getMST();
- System.out.printf("\t[Info] MST:\n%s\n", c);
- }
- }
Other algorithms for this problem include Prim's algorithm and Kruskal's algorithm. Fast parallel algorithms can be obtained by combining Prim's algorithm with Borůvka's.[8]
A faster randomized minimum spanning tree algorithm based in part on Borůvka's algorithm due to Karger, Klein, and Tarjan runs in expected O(E) time.[9] The best known (deterministic) minimum spanning tree algorithm by Bernard Chazelle is also based in part on Borůvka's and runs in O(E α(E,V)) time, where α is the inverse of the Ackermann function.[10] These randomized and deterministic algorithms combine steps of Borůvka's algorithm, reducing the number of components that remain to be connected, with steps of a different type that reduce the number of edges between pairs of components.
Supplement:
* [ Data Structures with Java ] Section 25.6 : Minimum Spanning Tree
* [ 資料結構 小學堂 ] 圖形結構 : 擴張樹 (Prim)
* [ 資料結構 小學堂 ] 圖形結構 : 擴張樹 - 求最小成本擴張樹 (Kruskal 演算法)
沒有留言:
張貼留言