BAEL-3883: Boruvka's Algorithm for Minimum-Spanning Trees (#8816)

* BAEL-3883: Boruvka's Algorithm for Minimum-Spanning Trees

* BAEL-3883: checking in pom.xml
This commit is contained in:
Sampada 2020-03-06 21:19:00 +05:30 committed by GitHub
parent 99fa431fcc
commit 7a2c941024
9 changed files with 443 additions and 0 deletions

View File

@ -39,6 +39,16 @@
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-commons</artifactId>
<version>${junit.platform.version}</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
@ -66,6 +76,8 @@
<commons-codec.version>1.11</commons-codec.version>
<commons-math3.version>3.6.1</commons-math3.version>
<guava.version>28.1-jre</guava.version>
<jackson.version>2.10.2</jackson.version>
<junit.platform.version>1.6.0</junit.platform.version>
</properties>
</project>

View File

@ -0,0 +1,61 @@
package com.baeldung.algorithms.boruvka;
public class BoruvkaMST {
private static Tree mst = new Tree();
private static int totalWeight;
public BoruvkaMST(Graph graph) {
DisjointSet dSet = new DisjointSet(graph.getNodes());
// repeat at most log N times or until we have N-1 edges
for (int t = 1; t < graph.getNodes() && mst.getEdgeCount() < graph.getNodes() - 1; t = t + t) {
// foreach tree in forest, find closest edge
Edge[] closestEdgeArray = new Edge[graph.getNodes()];
for (Edge edge : graph.getAllEdges()) {
int first = edge.getFirst();
int second = edge.getSecond();
int firstParent = dSet.getParent(first);
int secondParent = dSet.getParent(second);
if (firstParent == secondParent) {
continue; // same tree
}
if (closestEdgeArray[firstParent] == null || edge.getWeight() < closestEdgeArray[firstParent].getWeight()) {
closestEdgeArray[firstParent] = edge;
}
if (closestEdgeArray[secondParent] == null || edge.getWeight() < closestEdgeArray[secondParent].getWeight()) {
closestEdgeArray[secondParent] = edge;
}
}
// add newly discovered edges to MST
for (int i = 0; i < graph.getNodes(); i++) {
Edge edge = closestEdgeArray[i];
if (edge != null) {
int first = edge.getFirst();
int second = edge.getSecond();
// don't add the same edge twice
if (dSet.getParent(first) != dSet.getParent(second)) {
mst.addEdge(edge);
totalWeight += edge.getWeight();
dSet.union(first, second);
}
}
}
}
}
public Iterable<Edge> getMST() {
return mst;
}
public int getTotalWeight() {
return totalWeight;
}
public String toString() {
return "MST: " + mst.toString() + " | Total Weight: " + totalWeight;
}
}

View File

@ -0,0 +1,49 @@
package com.baeldung.algorithms.boruvka;
import java.util.Arrays;
public class DisjointSet {
private int[] nodeParents;
private int[] nodeRanks;
public DisjointSet(int n) {
nodeParents = new int[n];
nodeRanks = new int[n];
for (int i = 0; i < n; i++) {
nodeParents[i] = i;
nodeRanks[i] = 0;
}
}
public int getParent(int node) {
while (node != nodeParents[node]) {
node = nodeParents[node];
}
return node;
}
public void union(int node1, int node2) {
int node1Parent = getParent(node1);
int node2Parent = getParent(node2);
if (node1Parent == node2Parent) {
return;
}
if (nodeRanks[node1Parent] < nodeRanks[node2Parent]) {
nodeParents[node1Parent] = node2Parent;
}
else if (nodeRanks[node1Parent] > nodeRanks[node2Parent]) {
nodeParents[node2Parent] = node1Parent;
}
else {
nodeParents[node2Parent] = node1Parent;
nodeRanks[node1Parent]++;
}
}
public String toString() {
return "Parent: " + Arrays.toString(nodeParents) + "Rank: " + Arrays.toString(nodeRanks);
}
}

View File

@ -0,0 +1,40 @@
package com.baeldung.algorithms.boruvka;
public class Edge {
private final int first;
private final int second;
private final int weight;
public Edge(int first, int second, int weight) {
this.first = first;
this.second = second;
this.weight = weight;
}
public double getWeight() {
return weight;
}
public int getFirst() {
return first;
}
public int getSecond() {
return second;
}
public int getOtherNode(int firstNode) {
int secondNode = 0;
if (firstNode == first)
secondNode = second;
else if (firstNode == second)
secondNode = first;
return secondNode;
}
public String toString() {
return String.format("%d-%d %d", first, second, weight);
}
}

View File

@ -0,0 +1,64 @@
package com.baeldung.algorithms.boruvka;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
public class Graph {
private int nodes;
private int edges;
private Tree[] trees;
public Graph(Input jsonGraph) throws JsonParseException, JsonMappingException, IOException {
nodes = jsonGraph.getNodes();
trees = (Tree[]) new Tree[nodes];
for (int i = 0; i < nodes; i++) {
trees[i] = new Tree();
}
int edgesFromInput = jsonGraph.getEdges();
for (int i = 0; i < edgesFromInput; i++) {
int first = jsonGraph.getEdgeList()
.get(i)
.getFirst();
int second = jsonGraph.getEdgeList()
.get(i)
.getSecond();
int weight = jsonGraph.getEdgeList()
.get(i)
.getWeight();
Edge edge = new Edge(first, second, weight);
trees[first].addEdge(edge);
trees[second].addEdge(edge);
edges++;
}
}
public int getNodes() {
return nodes;
}
public int getEdges() {
return edges;
}
public Iterable<Edge> iterableTree(int i) {
return trees[i];
}
public Iterable<Edge> getAllEdges() {
Iterable<Edge> list = new Tree();
for (int i = 0; i < nodes; i++) {
for (Edge edge : iterableTree(i)) {
if (edge.getOtherNode(i) > i) {
((Tree) list).addEdge(edge);
}
}
}
return list;
}
}

View File

@ -0,0 +1,65 @@
package com.baeldung.algorithms.boruvka;
import java.util.List;
public class Input {
private int nodes;
private int edges;
private List<E> edgeList;
public int getNodes() {
return nodes;
}
public void setNodes(int nodes) {
this.nodes = nodes;
}
public int getEdges() {
return edges;
}
public void setEdges(int edges) {
this.edges = edges;
}
public List<E> getEdgeList() {
return edgeList;
}
public void setEdgeList(List<E> edgeList) {
this.edgeList = edgeList;
}
static class E {
private int first;
private int second;
private int weight;
public int getFirst() {
return first;
}
public void setFirst(int first) {
this.first = first;
}
public int getSecond() {
return second;
}
public void setSecond(int second) {
this.second = second;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
}
}

View File

@ -0,0 +1,63 @@
package com.baeldung.algorithms.boruvka;
import java.util.Iterator;
public class Tree implements Iterable<Edge> {
private Node root;
private int edgeCount;
private static class Node {
private Edge edge;
private Node next;
public String toString() {
String nextStr = next != null ? next.toString() : "";
return edge.toString() + " | " + nextStr;
}
}
public Tree() {
root = null;
edgeCount = 0;
}
public int getEdgeCount() {
return edgeCount;
}
public void addEdge(Edge edge) {
Node oldRoot = root;
root = new Node();
root.edge = edge;
root.next = oldRoot;
edgeCount++;
}
public String toString() {
String rootStr = root != null ? root.toString() : "";
return "Tree: " + rootStr + "Size: " + edgeCount;
}
public Iterator<Edge> iterator() {
return new LinkedIterator(root);
}
private class LinkedIterator implements Iterator<Edge> {
private Node current;
public LinkedIterator(Node root) {
current = root;
}
public boolean hasNext() {
return current != null;
}
public Edge next() {
Edge edge = current.edge;
current = current.next;
return edge;
}
}
}

View File

@ -0,0 +1,48 @@
package com.baeldung.algorithms.boruvka;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.junit.Before;
import org.junit.Test;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class BoruvkaUnitTest {
private Input input;
private static String INPUT_JSON = "/input.json";
@Before
public void convertInputJsonToObject() throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
StringBuilder jsonStr = new StringBuilder();
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(BoruvkaMST.class.getResourceAsStream(INPUT_JSON)))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
jsonStr.append(line)
.append("\n");
}
}
input = mapper.readValue(jsonStr.toString(), Input.class);
}
@Test
public void givenInputGraph_whenBoruvkaPerformed_thenMinimumSpanningTree() throws JsonParseException, JsonMappingException, IOException {
Graph graph = new Graph(input);
BoruvkaMST boruvkaMST = new BoruvkaMST(graph);
Tree mst = (Tree) boruvkaMST.getMST();
assertEquals(30, boruvkaMST.getTotalWeight());
assertEquals(4, mst.getEdgeCount());
}
}

View File

@ -0,0 +1,41 @@
{
"nodes": 5,
"edges": 7,
"edgeList": [
{
"first": 0,
"second": 1,
"weight": 8
},
{
"first": 0,
"second": 2,
"weight": 5
},
{
"first": 1,
"second": 2,
"weight": 9
},
{
"first": 1,
"second": 3,
"weight": 11
},
{
"first": 2,
"second": 3,
"weight": 15
},
{
"first": 2,
"second": 4,
"weight": 10
},
{
"first": 3,
"second": 4,
"weight": 7
}
]
}