From 060557608c8a11bdb31e707eedc41e91b2cf9af6 Mon Sep 17 00:00:00 2001 From: Sampada <46674082+sampada07@users.noreply.github.com> Date: Tue, 24 Mar 2020 08:37:48 +0530 Subject: [PATCH] BAEL-3883: Boruvka's minimum spanning tree, moved code to newer module (#8949) algorithms-miscellaneous-6 --- algorithms-miscellaneous-5/pom.xml | 6 -- .../algorithms/boruvka/BoruvkaMST.java | 61 -------------- .../algorithms/boruvka/DisjointSet.java | 49 ----------- .../com/baeldung/algorithms/boruvka/Edge.java | 40 --------- .../baeldung/algorithms/boruvka/Graph.java | 64 -------------- .../baeldung/algorithms/boruvka/Input.java | 65 -------------- .../com/baeldung/algorithms/boruvka/Tree.java | 63 -------------- .../algorithms/boruvka/BoruvkaUnitTest.java | 48 ----------- algorithms-miscellaneous-6/pom.xml | 12 +++ .../algorithms/boruvka/BoruvkaMST.java | 84 +++++++++++++++++++ .../algorithms/boruvka/UnionFind.java | 39 +++++++++ .../algorithms/boruvka/BoruvkaUnitTest.java | 37 ++++++++ 12 files changed, 172 insertions(+), 396 deletions(-) delete mode 100644 algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/BoruvkaMST.java delete mode 100644 algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/DisjointSet.java delete mode 100644 algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/Edge.java delete mode 100644 algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/Graph.java delete mode 100644 algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/Input.java delete mode 100644 algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/Tree.java delete mode 100644 algorithms-miscellaneous-5/src/test/java/com/baeldung/algorithms/boruvka/BoruvkaUnitTest.java create mode 100644 algorithms-miscellaneous-6/src/main/java/com/baeldung/algorithms/boruvka/BoruvkaMST.java create mode 100644 algorithms-miscellaneous-6/src/main/java/com/baeldung/algorithms/boruvka/UnionFind.java create mode 100644 algorithms-miscellaneous-6/src/test/java/com/baeldung/algorithms/boruvka/BoruvkaUnitTest.java diff --git a/algorithms-miscellaneous-5/pom.xml b/algorithms-miscellaneous-5/pom.xml index f2db71a6da..4131e1791d 100644 --- a/algorithms-miscellaneous-5/pom.xml +++ b/algorithms-miscellaneous-5/pom.xml @@ -41,11 +41,6 @@ guava ${guava.version} - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} - org.junit.platform junit-platform-commons @@ -78,7 +73,6 @@ 1.11 3.6.1 28.1-jre - 2.10.2 1.6.0 diff --git a/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/BoruvkaMST.java b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/BoruvkaMST.java deleted file mode 100644 index 0f8bc5b296..0000000000 --- a/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/BoruvkaMST.java +++ /dev/null @@ -1,61 +0,0 @@ -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 getMST() { - return mst; - } - - public int getTotalWeight() { - return totalWeight; - } - - public String toString() { - return "MST: " + mst.toString() + " | Total Weight: " + totalWeight; - } - -} diff --git a/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/DisjointSet.java b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/DisjointSet.java deleted file mode 100644 index 7769686e36..0000000000 --- a/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/DisjointSet.java +++ /dev/null @@ -1,49 +0,0 @@ -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); - } - -} diff --git a/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/Edge.java b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/Edge.java deleted file mode 100644 index 6ee136fc48..0000000000 --- a/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/Edge.java +++ /dev/null @@ -1,40 +0,0 @@ -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); - } - -} diff --git a/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/Graph.java b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/Graph.java deleted file mode 100644 index e899007dfa..0000000000 --- a/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/Graph.java +++ /dev/null @@ -1,64 +0,0 @@ -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 iterableTree(int i) { - return trees[i]; - } - - public Iterable getAllEdges() { - Iterable 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; - } -} diff --git a/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/Input.java b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/Input.java deleted file mode 100644 index b52720d5e9..0000000000 --- a/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/Input.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.baeldung.algorithms.boruvka; - -import java.util.List; - -public class Input { - private int nodes; - private int edges; - private List 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 getEdgeList() { - return edgeList; - } - - public void setEdgeList(List 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; - } - - } - -} diff --git a/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/Tree.java b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/Tree.java deleted file mode 100644 index cc28233e07..0000000000 --- a/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/boruvka/Tree.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.baeldung.algorithms.boruvka; - -import java.util.Iterator; - -public class Tree implements Iterable { - 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 iterator() { - return new LinkedIterator(root); - } - - private class LinkedIterator implements Iterator { - 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; - } - } - -} diff --git a/algorithms-miscellaneous-5/src/test/java/com/baeldung/algorithms/boruvka/BoruvkaUnitTest.java b/algorithms-miscellaneous-5/src/test/java/com/baeldung/algorithms/boruvka/BoruvkaUnitTest.java deleted file mode 100644 index 1d03d2d4d9..0000000000 --- a/algorithms-miscellaneous-5/src/test/java/com/baeldung/algorithms/boruvka/BoruvkaUnitTest.java +++ /dev/null @@ -1,48 +0,0 @@ -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()); - } - -} diff --git a/algorithms-miscellaneous-6/pom.xml b/algorithms-miscellaneous-6/pom.xml index 5daa000ea3..fda9cf10f9 100644 --- a/algorithms-miscellaneous-6/pom.xml +++ b/algorithms-miscellaneous-6/pom.xml @@ -11,5 +11,17 @@ parent-modules 1.0.0-SNAPSHOT + + + + com.google.guava + guava + ${guava.version} + + + + + 28.1-jre + diff --git a/algorithms-miscellaneous-6/src/main/java/com/baeldung/algorithms/boruvka/BoruvkaMST.java b/algorithms-miscellaneous-6/src/main/java/com/baeldung/algorithms/boruvka/BoruvkaMST.java new file mode 100644 index 0000000000..7dae21648c --- /dev/null +++ b/algorithms-miscellaneous-6/src/main/java/com/baeldung/algorithms/boruvka/BoruvkaMST.java @@ -0,0 +1,84 @@ +package com.baeldung.algorithms.boruvka; + +import com.google.common.graph.EndpointPair; +import com.google.common.graph.MutableValueGraph; +import com.google.common.graph.ValueGraphBuilder; + +public class BoruvkaMST { + + private static MutableValueGraph mst = ValueGraphBuilder.undirected() + .build(); + private static int totalWeight; + + public BoruvkaMST(MutableValueGraph graph) { + + int size = graph.nodes().size(); + + UnionFind uf = new UnionFind(size); + + // repeat at most log N times or until we have N-1 edges + for (int t = 1; t < size && mst.edges().size() < size - 1; t = t + t) { + + EndpointPair[] closestEdgeArray = new EndpointPair[size]; + + // foreach tree in graph, find closest edge + for (EndpointPair edge : graph.edges()) { + int u = edge.nodeU(); + int v = edge.nodeV(); + int uParent = uf.find(u); + int vParent = uf.find(v); + if (uParent == vParent) { + continue; // same tree + } + + int weight = graph.edgeValueOrDefault(u, v, 0); + + if (closestEdgeArray[uParent] == null) { + closestEdgeArray[uParent] = edge; + } + if (closestEdgeArray[vParent] == null) { + closestEdgeArray[vParent] = edge; + } + + int uParentWeight = graph.edgeValueOrDefault(closestEdgeArray[uParent].nodeU(), closestEdgeArray[uParent].nodeV(), 0); + int vParentWeight = graph.edgeValueOrDefault(closestEdgeArray[vParent].nodeU(), closestEdgeArray[vParent].nodeV(), 0); + + if (weight < uParentWeight) { + closestEdgeArray[uParent] = edge; + } + if (weight < vParentWeight) { + closestEdgeArray[vParent] = edge; + } + } + + // add newly discovered edges to MST + for (int i = 0; i < size; i++) { + EndpointPair edge = closestEdgeArray[i]; + if (edge != null) { + int u = edge.nodeU(); + int v = edge.nodeV(); + int weight = graph.edgeValueOrDefault(u, v, 0); + // don't add the same edge twice + if (uf.find(u) != uf.find(v)) { + mst.putEdgeValue(u, v, weight); + totalWeight += weight; + uf.union(u, v); + } + } + } + } + } + + public MutableValueGraph getMST() { + return mst; + } + + public int getTotalWeight() { + return totalWeight; + } + + public String toString() { + return "MST: " + mst.toString() + " | Total Weight: " + totalWeight; + } + +} diff --git a/algorithms-miscellaneous-6/src/main/java/com/baeldung/algorithms/boruvka/UnionFind.java b/algorithms-miscellaneous-6/src/main/java/com/baeldung/algorithms/boruvka/UnionFind.java new file mode 100644 index 0000000000..33c9c4a4c8 --- /dev/null +++ b/algorithms-miscellaneous-6/src/main/java/com/baeldung/algorithms/boruvka/UnionFind.java @@ -0,0 +1,39 @@ +package com.baeldung.algorithms.boruvka; + +public class UnionFind { + private int[] parents; + private int[] ranks; + + public UnionFind(int n) { + parents = new int[n]; + ranks = new int[n]; + for (int i = 0; i < n; i++) { + parents[i] = i; + ranks[i] = 0; + } + } + + public int find(int u) { + while (u != parents[u]) { + u = parents[u]; + } + return u; + } + + public void union(int u, int v) { + int uParent = find(u); + int vParent = find(v); + if (uParent == vParent) { + return; + } + + if (ranks[uParent] < ranks[vParent]) { + parents[uParent] = vParent; + } else if (ranks[uParent] > ranks[vParent]) { + parents[vParent] = uParent; + } else { + parents[vParent] = uParent; + ranks[uParent]++; + } + } +} diff --git a/algorithms-miscellaneous-6/src/test/java/com/baeldung/algorithms/boruvka/BoruvkaUnitTest.java b/algorithms-miscellaneous-6/src/test/java/com/baeldung/algorithms/boruvka/BoruvkaUnitTest.java new file mode 100644 index 0000000000..e61e1e668d --- /dev/null +++ b/algorithms-miscellaneous-6/src/test/java/com/baeldung/algorithms/boruvka/BoruvkaUnitTest.java @@ -0,0 +1,37 @@ +package com.baeldung.algorithms.boruvka; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +import com.google.common.graph.MutableValueGraph; +import com.google.common.graph.ValueGraphBuilder; + +public class BoruvkaUnitTest { + + private MutableValueGraph graph; + + @Before + public void setup() { + graph = ValueGraphBuilder.undirected() + .build(); + graph.putEdgeValue(0, 1, 8); + graph.putEdgeValue(0, 2, 5); + graph.putEdgeValue(1, 2, 9); + graph.putEdgeValue(1, 3, 11); + graph.putEdgeValue(2, 3, 15); + graph.putEdgeValue(2, 4, 10); + graph.putEdgeValue(3, 4, 7); + } + + @Test + public void givenInputGraph_whenBoruvkaPerformed_thenMinimumSpanningTree() { + BoruvkaMST boruvkaMST = new BoruvkaMST(graph); + MutableValueGraph mst = boruvkaMST.getMST(); + + assertEquals(30, boruvkaMST.getTotalWeight()); + assertEquals(4, mst.edges().size()); + } + +}