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);
}
}