/** * parallel MST algorithm based on Boruvka's algorithm. * * @param <E> value type of the node in the target graph * @param graph the graph to compute minimum spanning tree * @param pool external thread pool * @return a minimum spanning tree in the form of undirected graph * @throws InterruptedException thrown exception if be interrupted * @throws ExecutionException Exception thrown when attempting to retrieve the result of a task * that aborted by throwing an exception */ public static <E> Graph<E> getMST(final UndirectedGraph graph, ExecutorService pool) throws InterruptedException, ExecutionException { if (!(graph instanceof UndirectedGraph)) { throw new IllegalArgumentException("graph is not an undirected graph"); } final Graph<E> mst = new UndirectedGraph<E>(); Collection<Node<E>> nodes = graph.getAllNodes(); final Collection<Node<E>> graphNodes = graph.getAllNodes(); int size = nodes.size(); Runnable[] tasks = new Runnable[size]; int index = 0; final ConcurrentHashMap<Node<E>, Collection<Edge<E>>> edges = new ConcurrentHashMap<Node<E>, Collection<Edge<E>>>(); // step 0: init , get all the edge list and store them in map edges and // add all node into mst Iterator<Node<E>> it = nodes.iterator(); while (it.hasNext()) { final Node<E> n = it.next(); tasks[index++] = new Runnable() { public void run() { Collection<Edge<E>> edge = graph.getLinkedEdges(n); edges.put(n, edge); mst.addNode(n.getValue()); } }; } runTasks(tasks, 0, index, pool); final AtomicBoolean finish = new AtomicBoolean(false); final ConcurrentHashMap<Node<E>, Node<E>> roots = new ConcurrentHashMap<Node<E>, Node<E>>(); while ((size = (nodes = edges.keySet()).size()) > 1) { // System.out.println("222 " + size); Iterator<Node<E>> iter = nodes.iterator(); index = 0; // final CountDownLatch wait = new CountDownLatch(size); while (iter.hasNext()) { final Node<E> cur = iter.next(); // step 1:find lightest edge to each vertex , add it to // the new graph and remove it tasks[index++] = new Runnable() { public void run() { Edge<E> e = getMinimumEdgeWith(edges, cur); if (e == null) { finish.set(true); return; } Node<E> end = e.getEnd(); Node<E> start = e.getStart(); Node<E> n = roots.get(end); Node<E> c = roots.get(start); if (n == null) n = end; if (c == null) c = start; // merge the two trees. we vote the node with small // compare as root int cmp = n.compareTo(c); // if (cmp > 0) { // Node<E> v = roots.putIfAbsent(n, c); // } else if (cmp < 0) { // Node<E> v = roots.putIfAbsent(c, n); // } if (cmp != 0) mst.addEdge(start, end, e.getWeight()); } }; } runTasks(tasks, 0, index, pool); if (finish.get()) throw new IllegalArgumentException("graph is not a connected graph"); // step 2: find parent Enumeration<Node<E>> enu = roots.keys(); index = 0; while (enu.hasMoreElements()) { // System.out.println("444"); final Node<E> cur = enu.nextElement(); tasks[index++] = new Runnable() { public void run() { Node<E> p = cur; Node<E> tmp = cur; do { p = tmp; tmp = roots.get(tmp); } while (tmp != null); // TODO GZ:sometime infinite // loop here if (cur != p) { roots.put(cur, p); } } }; } runTasks(tasks, 0, index, pool); // step 3: rename vertex in original graph and remove the edges; index = 0; iter = graphNodes.iterator(); while (iter.hasNext()) { // System.out.println("555"); final Node<E> cur = iter.next(); tasks[index++] = new Runnable() { public void run() { Node<E> p = roots.get(cur); // for every parent, add all children's edgelist and // remove all the inner edge if (p == null) { Collection<Edge<E>> coll = edges.get(cur); HashSet<Node<E>> children = new HashSet<Node<E>>(); Iterator<Node<E>> it = graphNodes.iterator(); while (it.hasNext()) { Node<E> n = it.next(); if (roots.get(n) == cur) { children.add(n); if (edges.containsKey(n)) { coll.addAll(edges.get(n)); edges.remove(n); } } } Iterator<Edge<E>> iterator = coll.iterator(); while (iterator.hasNext()) { Edge<E> e = iterator.next(); Node<E> end = e.getEnd(); if (end.equals(cur) || children.contains(end)) { iterator.remove(); } } } } }; } runTasks(tasks, 0, index, pool); } // remove loop edge in MST nodes = mst.getAllNodes(); index = 0; Iterator<Node<E>> iter = nodes.iterator(); while (iter.hasNext()) { final Node<E> cur = iter.next(); tasks[index++] = new Runnable() { public void run() { // use a hashset to find the same node, pay space for time HashSet<Node<E>> sets = new HashSet<Node<E>>(); Collection<AdjacentNode<E>> lns = mst.getLinkedNodes(cur); Iterator<AdjacentNode<E>> iterAdj = lns.iterator(); AdjacentNode<E> adj; while (iterAdj.hasNext()) { adj = iterAdj.next(); Node<E> tar = adj.getNode(); if (!sets.add(tar)) { iterAdj.remove(); } } } }; } runTasks(tasks, 0, index, pool); return mst; }