package com.jwetherell.algorithms.graph; import com.jwetherell.algorithms.data_structures.Graph; import java.util.*; /** * Kruskal's minimum spanning tree. Only works on undirected graphs. It finds a * subset of the edges that forms a tree that includes every vertex, where the * total weight of all the edges in the tree is minimized. *

* https://en.wikipedia.org/wiki/Kruskal%27s_algorithm * * @author Bartlomiej Drozd * @author Justin Wetherell */ public class Kruskal { private Kruskal() { } public static Graph.CostPathPair getMinimumSpanningTree(Graph graph) { if (graph == null) throw (new NullPointerException("Graph must be non-NULL.")); // Kruskal's algorithm only works on undirected graphs if (graph.getType() == Graph.TYPE.DIRECTED) throw (new IllegalArgumentException("Undirected graphs only.")); int cost = 0; final List> path = new ArrayList>(); // Prepare data to store information which part of tree given vertex is HashMap, HashSet>> membershipMap = new HashMap, HashSet>>(); for (Graph.Vertex v : graph.getVertices()) { HashSet> set = new HashSet>(); set.add(v); membershipMap.put(v, set); } // We make queue of edges to consider all of them, starting with edge with the lowest cost, // it is important that Edge's class comparator is not natural (ex. sorting is from the biggest to the lowest) PriorityQueue> edgeQueue = new PriorityQueue>(graph.getEdges()); while (!edgeQueue.isEmpty()) { Graph.Edge edge = edgeQueue.poll(); // If from vertex and to vertex are from different parts of tree then add this edge to result and union vertices' parts if (!isTheSamePart(edge.getFromVertex(), edge.getToVertex(), membershipMap)) { union(edge.getFromVertex(), edge.getToVertex(), membershipMap); path.add(edge); cost += edge.getCost(); } } return (new Graph.CostPathPair(cost, path)); } private static boolean isTheSamePart(Graph.Vertex v1, Graph.Vertex v2, HashMap, HashSet>> membershipMap) { return membershipMap.get(v1) == membershipMap.get(v2); } private static void union(Graph.Vertex v1, Graph.Vertex v2, HashMap, HashSet>> membershipMap) { HashSet> firstSet = membershipMap.get(v1); //first set is the bigger set HashSet> secondSet = membershipMap.get(v2); // we want to include smaller set into bigger, so second set cannot be bigger than first if (secondSet.size() > firstSet.size()) { HashSet> tempSet = firstSet; firstSet = secondSet; secondSet = tempSet; } // changing part membership of each vertex from smaller set for (Graph.Vertex v : secondSet) { membershipMap.put(v, firstSet); } // adding all vertices from smaller set to bigger one firstSet.addAll(secondSet); } }