From 9839130ecdf7c88911692d0b77171047f7635dd3 Mon Sep 17 00:00:00 2001 From: anandraj095 Date: Wed, 17 Dec 2025 12:06:43 +0530 Subject: [PATCH 1/3] Add Kruskal's Minimum Spanning Tree algorithm using Union-Find --- .../java/com/thealgorithms/graph/Edge.java | 35 ++++++++ .../com/thealgorithms/graph/KruskalMST.java | 90 +++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 src/main/java/com/thealgorithms/graph/Edge.java create mode 100644 src/main/java/com/thealgorithms/graph/KruskalMST.java diff --git a/src/main/java/com/thealgorithms/graph/Edge.java b/src/main/java/com/thealgorithms/graph/Edge.java new file mode 100644 index 000000000000..279a6662b7cc --- /dev/null +++ b/src/main/java/com/thealgorithms/graph/Edge.java @@ -0,0 +1,35 @@ +package com.thealgorithms.graph; + +/** + * Represents an edge in an undirected weighted graph. + */ +public class Edge implements Comparable { + + public final int source; + public final int destination; + public final int weight; + + /** + * Constructs an edge with given source, destination, and weight. + * + * @param source the source vertex + * @param destination the destination vertex + * @param weight the weight of the edge + */ + public Edge(final int source, final int destination, final int weight) { + this.source = source; + this.destination = destination; + this.weight = weight; + } + + /** + * Compares edges based on their weight. + * + * @param other the edge to compare with + * @return comparison result + */ + @Override + public int compareTo(final Edge other) { + return Integer.compare(this.weight, other.weight); + } +} diff --git a/src/main/java/com/thealgorithms/graph/KruskalMST.java b/src/main/java/com/thealgorithms/graph/KruskalMST.java new file mode 100644 index 000000000000..983737119aa2 --- /dev/null +++ b/src/main/java/com/thealgorithms/graph/KruskalMST.java @@ -0,0 +1,90 @@ +package com.thealgorithms.graph; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Implementation of Kruskal's Algorithm to find + * the Minimum Spanning Tree (MST) of a connected, + * undirected, weighted graph. + */ +public final class KruskalMST { + + private KruskalMST() { + // Utility class + } + + /** + * Finds the Minimum Spanning Tree using Kruskal's Algorithm. + * + * @param vertices number of vertices in the graph + * @param edges list of all edges in the graph + * @return list of edges forming the MST + * @throws IllegalArgumentException if vertices <= 0 + */ + public static List findMST(final int vertices, final List edges) { + if (vertices <= 0) { + throw new IllegalArgumentException("Number of vertices must be positive"); + } + + final List mst = new ArrayList<>(); + final DisjointSetUnion dsu = new DisjointSetUnion(vertices); + + Collections.sort(edges); + + for (final Edge edge : edges) { + final int rootU = dsu.find(edge.source); + final int rootV = dsu.find(edge.destination); + + if (rootU != rootV) { + mst.add(edge); + dsu.union(rootU, rootV); + + if (mst.size() == vertices - 1) { + break; + } + } + } + + return mst; + } + + /** + * Disjoint Set Union (Union-Find) with + * path compression and union by rank. + */ + private static final class DisjointSetUnion { + + private final int[] parent; + private final int[] rank; + + private DisjointSetUnion(final int size) { + parent = new int[size]; + rank = new int[size]; + + for (int i = 0; i < size; i++) { + parent[i] = i; + rank[i] = 0; + } + } + + private int find(final int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + private void union(final int u, final int v) { + if (rank[u] < rank[v]) { + parent[u] = v; + } else if (rank[u] > rank[v]) { + parent[v] = u; + } else { + parent[v] = u; + rank[u]++; + } + } + } +} From 5b721b217e884c544234d7ce306017447b650e98 Mon Sep 17 00:00:00 2001 From: anandraj095 Date: Wed, 17 Dec 2025 12:41:40 +0530 Subject: [PATCH 2/3] Add Kruskal's Minimum Spanning Tree algorithm Test --- .../thealgorithms/graph/KruskalMSTTest.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/test/java/com/thealgorithms/graph/KruskalMSTTest.java diff --git a/src/test/java/com/thealgorithms/graph/KruskalMSTTest.java b/src/test/java/com/thealgorithms/graph/KruskalMSTTest.java new file mode 100644 index 000000000000..b09099f5fdf1 --- /dev/null +++ b/src/test/java/com/thealgorithms/graph/KruskalMSTTest.java @@ -0,0 +1,50 @@ +package com.thealgorithms.graph; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +/** + * Test cases for Kruskal's Minimum Spanning Tree algorithm. + */ +class KruskalMSTTest { + + @Test + void testFindMSTWithSimpleGraph() { + final int vertices = 4; + + final List edges = new ArrayList<>(); + edges.add(new Edge(0, 1, 10)); + edges.add(new Edge(0, 2, 6)); + edges.add(new Edge(0, 3, 5)); + edges.add(new Edge(1, 3, 15)); + edges.add(new Edge(2, 3, 4)); + + final List mst = KruskalMST.findMST(vertices, edges); + + assertNotNull(mst, "MST should not be null"); + assertEquals(vertices - 1, mst.size(), "MST should contain V-1 edges"); + + int totalWeight = 0; + for (final Edge edge : mst) { + totalWeight += edge.weight; + } + + assertEquals(19, totalWeight, "Total weight of MST is incorrect"); + } + + @Test + void testFindMSTWithSingleVertex() { + final int vertices = 1; + final List edges = new ArrayList<>(); + + final List mst = KruskalMST.findMST(vertices, edges); + + assertNotNull(mst, "MST should not be null"); + assertEquals(0, mst.size(), "MST of single vertex graph should be empty"); + } +} From 54b8922f51fb439376212d8d02136cd15c8de285 Mon Sep 17 00:00:00 2001 From: anandraj095 Date: Wed, 17 Dec 2025 13:01:37 +0530 Subject: [PATCH 3/3] Improve robustness and test coverage for Kruskal MST --- .../com/thealgorithms/graph/KruskalMST.java | 11 ++++++-- .../thealgorithms/graph/KruskalMSTTest.java | 28 +++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/thealgorithms/graph/KruskalMST.java b/src/main/java/com/thealgorithms/graph/KruskalMST.java index 983737119aa2..211acba79185 100644 --- a/src/main/java/com/thealgorithms/graph/KruskalMST.java +++ b/src/main/java/com/thealgorithms/graph/KruskalMST.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; /** * Implementation of Kruskal's Algorithm to find @@ -22,18 +23,22 @@ private KruskalMST() { * @param edges list of all edges in the graph * @return list of edges forming the MST * @throws IllegalArgumentException if vertices <= 0 + * @throws NullPointerException if edges is null */ public static List findMST(final int vertices, final List edges) { if (vertices <= 0) { throw new IllegalArgumentException("Number of vertices must be positive"); } + Objects.requireNonNull(edges, "Edges list must not be null"); + + final List sortedEdges = new ArrayList<>(edges); + Collections.sort(sortedEdges); + final List mst = new ArrayList<>(); final DisjointSetUnion dsu = new DisjointSetUnion(vertices); - Collections.sort(edges); - - for (final Edge edge : edges) { + for (final Edge edge : sortedEdges) { final int rootU = dsu.find(edge.source); final int rootV = dsu.find(edge.destination); diff --git a/src/test/java/com/thealgorithms/graph/KruskalMSTTest.java b/src/test/java/com/thealgorithms/graph/KruskalMSTTest.java index b09099f5fdf1..9b2506affc29 100644 --- a/src/test/java/com/thealgorithms/graph/KruskalMSTTest.java +++ b/src/test/java/com/thealgorithms/graph/KruskalMSTTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.ArrayList; import java.util.List; @@ -47,4 +48,31 @@ void testFindMSTWithSingleVertex() { assertNotNull(mst, "MST should not be null"); assertEquals(0, mst.size(), "MST of single vertex graph should be empty"); } + + @Test + void testInvalidVertexCountThrowsException() { + final List edges = new ArrayList<>(); + + assertThrows( + IllegalArgumentException.class, + () -> KruskalMST.findMST(0, edges), + "Expected exception for non-positive vertex count" + ); + } + + @Test + void testPathCompressionScenario() { + final int vertices = 5; + + final List edges = new ArrayList<>(); + edges.add(new Edge(0, 1, 1)); + edges.add(new Edge(1, 2, 2)); + edges.add(new Edge(2, 3, 3)); + edges.add(new Edge(3, 4, 4)); + + final List mst = KruskalMST.findMST(vertices, edges); + + assertNotNull(mst); + assertEquals(vertices - 1, mst.size(), "MST should contain V-1 edges"); + } }