-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
Kruskal.java
82 lines (64 loc) · 3.55 KB
/
Kruskal.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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.
* <p>
* @see <a href="https://en.wikipedia.org/wiki/Kruskal%27s_algorithm">Kruskal's Algorithm (Wikipedia)</a>
* <br>
* @author Bartlomiej Drozd <[email protected]>
* @author Justin Wetherell <[email protected]>
*/
public class Kruskal {
private Kruskal() { }
public static Graph.CostPathPair<Integer> getMinimumSpanningTree(Graph<Integer> 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<Graph.Edge<Integer>> path = new ArrayList<Graph.Edge<Integer>>();
// Prepare data to store information which part of tree given vertex is
HashMap<Graph.Vertex<Integer>, HashSet<Graph.Vertex<Integer>>> membershipMap = new HashMap<Graph.Vertex<Integer>, HashSet<Graph.Vertex<Integer>>>();
for (Graph.Vertex<Integer> v : graph.getVertices()) {
HashSet<Graph.Vertex<Integer>> set = new HashSet<Graph.Vertex<Integer>>();
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<Graph.Edge<Integer>> edgeQueue = new PriorityQueue<Graph.Edge<Integer>>(graph.getEdges());
while (!edgeQueue.isEmpty()) {
Graph.Edge<Integer> 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<Integer>(cost, path));
}
private static boolean isTheSamePart(Graph.Vertex<Integer> v1, Graph.Vertex<Integer> v2, HashMap<Graph.Vertex<Integer>, HashSet<Graph.Vertex<Integer>>> membershipMap) {
return membershipMap.get(v1) == membershipMap.get(v2);
}
private static void union(Graph.Vertex<Integer> v1, Graph.Vertex<Integer> v2, HashMap<Graph.Vertex<Integer>, HashSet<Graph.Vertex<Integer>>> membershipMap) {
HashSet<Graph.Vertex<Integer>> firstSet = membershipMap.get(v1); //first set is the bigger set
HashSet<Graph.Vertex<Integer>> 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<Graph.Vertex<Integer>> tempSet = firstSet;
firstSet = secondSet;
secondSet = tempSet;
}
// changing part membership of each vertex from smaller set
for (Graph.Vertex<Integer> v : secondSet) {
membershipMap.put(v, firstSet);
}
// adding all vertices from smaller set to bigger one
firstSet.addAll(secondSet);
}
}