diff --git a/algorithms-miscellaneous-5/pom.xml b/algorithms-miscellaneous-5/pom.xml
index 2f530958e3..95036da775 100644
--- a/algorithms-miscellaneous-5/pom.xml
+++ b/algorithms-miscellaneous-5/pom.xml
@@ -34,6 +34,11 @@
tradukisto
${tradukisto.version}
+
+ com.google.guava
+ guava
+ 28.1-jre
+
org.assertj
diff --git a/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/kruskal/CycleDetector.java b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/kruskal/CycleDetector.java
new file mode 100644
index 0000000000..dec0bcdd85
--- /dev/null
+++ b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/kruskal/CycleDetector.java
@@ -0,0 +1,72 @@
+package com.baeldung.algorithms.kruskal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CycleDetector {
+
+ List nodes;
+
+ public CycleDetector(int totalNodes) {
+ initDisjointSets(totalNodes);
+ }
+
+ public boolean detectCycle(Integer u, Integer v) {
+ Integer rootU = pathCompressionFind(u);
+ Integer rootV = pathCompressionFind(v);
+ if (rootU.equals(rootV)) {
+ return true;
+ }
+ unionByRank(rootU, rootV);
+ return false;
+ }
+
+ private void initDisjointSets(int totalNodes) {
+ nodes = new ArrayList<>(totalNodes);
+ for (int i = 0; i < totalNodes; i++) {
+ nodes.add(new DisjointSetInfo(i));
+ }
+ }
+
+ private Integer find(Integer node) {
+ Integer parent = nodes.get(node).getParentNode();
+ if (parent.equals(node)) {
+ return node;
+ } else {
+ return find(parent);
+ }
+ }
+
+ private Integer pathCompressionFind(Integer node) {
+ DisjointSetInfo setInfo = nodes.get(node);
+ Integer parent = setInfo.getParentNode();
+ if (parent.equals(node)) {
+ return node;
+ } else {
+ Integer parentNode = find(parent);
+ setInfo.setParentNode(parentNode);
+ return parentNode;
+ }
+ }
+
+ private void union(Integer rootU, Integer rootV) {
+ DisjointSetInfo setInfoU = nodes.get(rootU);
+ setInfoU.setParentNode(rootV);
+ }
+
+ private void unionByRank(int rootU, int rootV) {
+ DisjointSetInfo setInfoU = nodes.get(rootU);
+ DisjointSetInfo setInfoV = nodes.get(rootV);
+ int rankU = setInfoU.getRank();
+ int rankV = setInfoV.getRank();
+ if (rankU < rankV) {
+ setInfoU.setParentNode(rootV);
+ } else {
+ setInfoV.setParentNode(rootU);
+ if (rankU == rankV) {
+ setInfoU.setRank(rankU + 1);
+ }
+ }
+ }
+
+}
diff --git a/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/kruskal/DisjointSetInfo.java b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/kruskal/DisjointSetInfo.java
new file mode 100644
index 0000000000..ecdc42587a
--- /dev/null
+++ b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/kruskal/DisjointSetInfo.java
@@ -0,0 +1,28 @@
+package com.baeldung.algorithms.kruskal;
+
+public class DisjointSetInfo {
+
+ private Integer parentNode;
+ private int rank;
+
+ DisjointSetInfo(Integer nodeNumber) {
+ setParentNode(nodeNumber);
+ setRank(1);
+ }
+
+ public Integer getParentNode() {
+ return parentNode;
+ }
+
+ public void setParentNode(Integer parentNode) {
+ this.parentNode = parentNode;
+ }
+
+ public int getRank() {
+ return rank;
+ }
+
+ public void setRank(int rank) {
+ this.rank = rank;
+ }
+}
diff --git a/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/kruskal/Kruskal.java b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/kruskal/Kruskal.java
new file mode 100644
index 0000000000..da405679d1
--- /dev/null
+++ b/algorithms-miscellaneous-5/src/main/java/com/baeldung/algorithms/kruskal/Kruskal.java
@@ -0,0 +1,53 @@
+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 minSpanningTree(ValueGraph graph) {
+
+ return spanningTree(graph, true);
+ }
+
+ public ValueGraph maxSpanningTree(ValueGraph graph) {
+ return spanningTree(graph, false);
+ }
+
+ private ValueGraph spanningTree(ValueGraph graph, boolean minSpanningTree) {
+ Set> edges = graph.edges();
+ List> 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 totalNodes = graph.nodes().size();
+ CycleDetector cycleDetector = new CycleDetector(totalNodes);
+ int edgeCount = 0;
+
+ MutableValueGraph spanningTree = ValueGraphBuilder.undirected().build();
+ for (EndpointPair edge : edgeList) {
+ if (cycleDetector.detectCycle(edge.nodeU(), edge.nodeV())) {
+ continue;
+ }
+ spanningTree.putEdgeValue(edge.nodeU(), edge.nodeV(), graph.edgeValue(edge).get());
+ edgeCount++;
+ if (edgeCount == totalNodes - 1) {
+ break;
+ }
+ }
+ return spanningTree;
+ }
+
+}
diff --git a/algorithms-miscellaneous-5/src/test/java/com/baeldung/algorithms/kruskal/KruskalUnitTest.java b/algorithms-miscellaneous-5/src/test/java/com/baeldung/algorithms/kruskal/KruskalUnitTest.java
new file mode 100644
index 0000000000..a7206c6cd0
--- /dev/null
+++ b/algorithms-miscellaneous-5/src/test/java/com/baeldung/algorithms/kruskal/KruskalUnitTest.java
@@ -0,0 +1,67 @@
+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;
+import com.baeldung.algorithms.kruskal.Kruskal;
+
+public class KruskalUnitTest {
+
+ private MutableValueGraph 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 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 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));
+ }
+}