Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/main/java/com/thealgorithms/graph/Edge.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.thealgorithms.graph;

/**
* Represents an edge in an undirected weighted graph.
*/
public class Edge implements Comparable<Edge> {

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);
}
}
95 changes: 95 additions & 0 deletions src/main/java/com/thealgorithms/graph/KruskalMST.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.thealgorithms.graph;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
* 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
* @throws NullPointerException if edges is null
*/
public static List<Edge> findMST(final int vertices, final List<Edge> edges) {
if (vertices <= 0) {
throw new IllegalArgumentException("Number of vertices must be positive");
}

Objects.requireNonNull(edges, "Edges list must not be null");

final List<Edge> sortedEdges = new ArrayList<>(edges);
Collections.sort(sortedEdges);

final List<Edge> mst = new ArrayList<>();
final DisjointSetUnion dsu = new DisjointSetUnion(vertices);

for (final Edge edge : sortedEdges) {
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]++;
}
}
}
}
78 changes: 78 additions & 0 deletions src/test/java/com/thealgorithms/graph/KruskalMSTTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.thealgorithms.graph;

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;

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<Edge> 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<Edge> 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<Edge> edges = new ArrayList<>();

final List<Edge> mst = KruskalMST.findMST(vertices, edges);

assertNotNull(mst, "MST should not be null");
assertEquals(0, mst.size(), "MST of single vertex graph should be empty");
}

@Test
void testInvalidVertexCountThrowsException() {
final List<Edge> 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<Edge> 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<Edge> mst = KruskalMST.findMST(vertices, edges);

assertNotNull(mst);
assertEquals(vertices - 1, mst.size(), "MST should contain V-1 edges");
}
}