BAEL-3406 Kruskal’s Algorithm for Spanning Trees

This commit is contained in:
Gang 2019-12-16 23:46:22 -07:00
parent 9cd4a0a16e
commit 0df902da92
3 changed files with 159 additions and 0 deletions

View File

@ -34,6 +34,11 @@
<artifactId>tradukisto</artifactId>
<version>${tradukisto.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.1-jre</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>

View File

@ -0,0 +1,88 @@
package com.baeldung.algorithms.kruskal;
import com.google.common.graph.EndpointPair;
import com.google.common.graph.MutableValueGraph;
import com.google.common.graph.ValueGraph;
import com.google.common.graph.ValueGraphBuilder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
public class Kruskal {
public ValueGraph<Integer, Double> minSpanningTree(ValueGraph<Integer, Double> graph) {
return spanningTree(graph, true);
}
public ValueGraph<Integer, Double> maxSpanningTree(ValueGraph<Integer, Double> graph) {
return spanningTree(graph, false);
}
private ValueGraph<Integer, Double> spanningTree(ValueGraph<Integer, Double> graph, boolean minSpanningTree) {
Set<EndpointPair<Integer>> edges = graph.edges();
List<EndpointPair<Integer>> edgeList = new ArrayList<>(edges);
if (minSpanningTree) {
edgeList.sort(Comparator.comparing(e -> graph.edgeValue(e).get()));
} else {
edgeList.sort(Collections.reverseOrder(Comparator.comparing(e -> graph.edgeValue(e).get())));
}
int totalEdges = edgeList.size();
int totalNodes = graph.nodes().size();
int edgeCount = 0;
List<Integer> roots = new ArrayList<>(totalNodes);
List<Integer> sizes = new ArrayList<>(totalNodes);
for (int i = 0; i < totalNodes; i++) {
roots.add(i);
sizes.add(1);
}
MutableValueGraph<Integer, Double> spanningTree = ValueGraphBuilder.undirected().build();
for (int i = 0; i < totalEdges; i++) {
EndpointPair<Integer> edge = edgeList.get(i);
if (detectCycle(edge.nodeU(), edge.nodeV(), roots, sizes)) {
continue;
}
spanningTree.putEdgeValue(edge.nodeU(), edge.nodeV(), graph.edgeValue(edge).get());
edgeCount++;
if (edgeCount == totalNodes - 1) {
break;
}
}
return spanningTree;
}
private Integer find(Integer x, List<Integer> roots) {
Integer root = roots.get(x);
if (!root.equals(x)) {
roots.set(x, find(root, roots));
}
return roots.get(x);
}
private void unionBySize(Integer rootU, Integer rootV, List<Integer> roots, List<Integer> sizes) {
Integer total = sizes.get(rootU) + sizes.get(rootV);
if (sizes.get(rootU) < sizes.get(rootV)) {
roots.set(rootU, rootV);
sizes.set(rootV, total);
} else {
roots.set(rootV, rootU);
sizes.set(rootU, total);
}
}
private boolean detectCycle(Integer u, Integer v, List<Integer> roots, List<Integer> sizes) {
Integer rootU = find(u, roots);
Integer rootV = find(v, roots);
if (rootU.equals(rootV)) {
return true;
}
unionBySize(rootU, rootV, roots, sizes);
return false;
}
}

View File

@ -0,0 +1,66 @@
package com.baeldung.algorithms.kruskal;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import com.google.common.graph.MutableValueGraph;
import com.google.common.graph.ValueGraph;
import com.google.common.graph.ValueGraphBuilder;
public class KruskalUnitTest {
private MutableValueGraph<Integer, Double> graph;
@Before
public void setup() {
graph = ValueGraphBuilder.undirected().build();
graph.putEdgeValue(0, 1, 8.0);
graph.putEdgeValue(0, 2, 5.0);
graph.putEdgeValue(1, 2, 9.0);
graph.putEdgeValue(1, 3, 11.0);
graph.putEdgeValue(2, 3, 15.0);
graph.putEdgeValue(2, 4, 10.0);
graph.putEdgeValue(3, 4, 7.0);
}
@Test
public void givenGraph_whenMinimumSpanningTree_thenOutputCorrectResult() {
final Kruskal kruskal = new Kruskal();
ValueGraph<Integer, Double> spanningTree = kruskal.minSpanningTree(graph);
assertTrue(spanningTree.hasEdgeConnecting(0, 1));
assertTrue(spanningTree.hasEdgeConnecting(0, 2));
assertTrue(spanningTree.hasEdgeConnecting(2, 4));
assertTrue(spanningTree.hasEdgeConnecting(3, 4));
assertEquals(graph.edgeValue(0, 1), spanningTree.edgeValue(0, 1));
assertEquals(graph.edgeValue(0, 2), spanningTree.edgeValue(0, 2));
assertEquals(graph.edgeValue(2, 4), spanningTree.edgeValue(2, 4));
assertEquals(graph.edgeValue(3, 4), spanningTree.edgeValue(3, 4));
assertFalse(spanningTree.hasEdgeConnecting(1, 2));
assertFalse(spanningTree.hasEdgeConnecting(1, 3));
assertFalse(spanningTree.hasEdgeConnecting(2, 3));
}
@Test
public void givenGraph_whenMaximumSpanningTree_thenOutputCorrectResult() {
final Kruskal kruskal = new Kruskal();
ValueGraph<Integer, Double> spanningTree = kruskal.maxSpanningTree(graph);
assertTrue(spanningTree.hasEdgeConnecting(0, 1));
assertTrue(spanningTree.hasEdgeConnecting(1, 3));
assertTrue(spanningTree.hasEdgeConnecting(2, 3));
assertTrue(spanningTree.hasEdgeConnecting(2, 4));
assertEquals(graph.edgeValue(0, 1), spanningTree.edgeValue(0, 1));
assertEquals(graph.edgeValue(1, 3), spanningTree.edgeValue(1, 3));
assertEquals(graph.edgeValue(2, 3), spanningTree.edgeValue(2, 3));
assertEquals(graph.edgeValue(2, 4), spanningTree.edgeValue(2, 4));
assertFalse(spanningTree.hasEdgeConnecting(0, 2));
assertFalse(spanningTree.hasEdgeConnecting(1, 2));
assertFalse(spanningTree.hasEdgeConnecting(3, 4));
}
}