Skip to content

Commit 0feb416

Browse files
authored
Enhance docs, add tests in Kruskal (#5967)
1 parent 578e5a7 commit 0feb416

File tree

3 files changed

+161
-59
lines changed

3 files changed

+161
-59
lines changed

DIRECTORY.md

+1
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,7 @@
812812
* [JohnsonsAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/JohnsonsAlgorithmTest.java)
813813
* [KahnsAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java)
814814
* [KosarajuTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/KosarajuTest.java)
815+
* [KruskalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/KruskalTest.java)
815816
* [TarjansAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithmTest.java)
816817
* [WelshPowellTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/WelshPowellTest.java)
817818
* hashmap

src/main/java/com/thealgorithms/datastructures/graphs/Kruskal.java

+48-59
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,34 @@
11
package com.thealgorithms.datastructures.graphs;
22

3-
// Problem -> Connect all the edges with the minimum cost.
4-
// Possible Solution -> Kruskal Algorithm (KA), KA finds the minimum-spanning-tree, which means, the
5-
// group of edges with the minimum sum of their weights that connect the whole graph.
6-
// The graph needs to be connected, because if there are nodes impossible to reach, there are no
7-
// edges that could connect every node in the graph.
8-
// KA is a Greedy Algorithm, because edges are analysed based on their weights, that is why a
9-
// Priority Queue is used, to take first those less weighted.
10-
// This implementations below has some changes compared to conventional ones, but they are explained
11-
// all along the code.
123
import java.util.Comparator;
134
import java.util.HashSet;
145
import java.util.PriorityQueue;
156

7+
/**
8+
* The Kruskal class implements Kruskal's Algorithm to find the Minimum Spanning Tree (MST)
9+
* of a connected, undirected graph. The algorithm constructs the MST by selecting edges
10+
* with the least weight, ensuring no cycles are formed, and using union-find to track the
11+
* connected components.
12+
*
13+
* <p><strong>Key Features:</strong></p>
14+
* <ul>
15+
* <li>The graph is represented using an adjacency list, where each node points to a set of edges.</li>
16+
* <li>Each edge is processed in ascending order of weight using a priority queue.</li>
17+
* <li>The algorithm stops when all nodes are connected or no more edges are available.</li>
18+
* </ul>
19+
*
20+
* <p><strong>Time Complexity:</strong> O(E log V), where E is the number of edges and V is the number of vertices.</p>
21+
*/
1622
public class Kruskal {
1723

18-
// Complexity: O(E log V) time, where E is the number of edges in the graph and V is the number
19-
// of vertices
20-
private static class Edge {
24+
/**
25+
* Represents an edge in the graph with a source, destination, and weight.
26+
*/
27+
static class Edge {
2128

22-
private int from;
23-
private int to;
24-
private int weight;
29+
int from;
30+
int to;
31+
int weight;
2532

2633
Edge(int from, int to, int weight) {
2734
this.from = from;
@@ -30,51 +37,30 @@ private static class Edge {
3037
}
3138
}
3239

33-
private static void addEdge(HashSet<Edge>[] graph, int from, int to, int weight) {
40+
/**
41+
* Adds an edge to the graph.
42+
*
43+
* @param graph the adjacency list representing the graph
44+
* @param from the source vertex of the edge
45+
* @param to the destination vertex of the edge
46+
* @param weight the weight of the edge
47+
*/
48+
static void addEdge(HashSet<Edge>[] graph, int from, int to, int weight) {
3449
graph[from].add(new Edge(from, to, weight));
3550
}
3651

37-
public static void main(String[] args) {
38-
HashSet<Edge>[] graph = new HashSet[7];
39-
for (int i = 0; i < graph.length; i++) {
40-
graph[i] = new HashSet<>();
41-
}
42-
addEdge(graph, 0, 1, 2);
43-
addEdge(graph, 0, 2, 3);
44-
addEdge(graph, 0, 3, 3);
45-
addEdge(graph, 1, 2, 4);
46-
addEdge(graph, 2, 3, 5);
47-
addEdge(graph, 1, 4, 3);
48-
addEdge(graph, 2, 4, 1);
49-
addEdge(graph, 3, 5, 7);
50-
addEdge(graph, 4, 5, 8);
51-
addEdge(graph, 5, 6, 9);
52-
53-
System.out.println("Initial Graph: ");
54-
for (int i = 0; i < graph.length; i++) {
55-
for (Edge edge : graph[i]) {
56-
System.out.println(i + " <-- weight " + edge.weight + " --> " + edge.to);
57-
}
58-
}
59-
60-
Kruskal k = new Kruskal();
61-
HashSet<Edge>[] solGraph = k.kruskal(graph);
62-
63-
System.out.println("\nMinimal Graph: ");
64-
for (int i = 0; i < solGraph.length; i++) {
65-
for (Edge edge : solGraph[i]) {
66-
System.out.println(i + " <-- weight " + edge.weight + " --> " + edge.to);
67-
}
68-
}
69-
}
70-
52+
/**
53+
* Kruskal's algorithm to find the Minimum Spanning Tree (MST) of a graph.
54+
*
55+
* @param graph the adjacency list representing the input graph
56+
* @return the adjacency list representing the MST
57+
*/
7158
public HashSet<Edge>[] kruskal(HashSet<Edge>[] graph) {
7259
int nodes = graph.length;
73-
int[] captain = new int[nodes];
74-
// captain of i, stores the set with all the connected nodes to i
60+
int[] captain = new int[nodes]; // Stores the "leader" of each node's connected component
7561
HashSet<Integer>[] connectedGroups = new HashSet[nodes];
7662
HashSet<Edge>[] minGraph = new HashSet[nodes];
77-
PriorityQueue<Edge> edges = new PriorityQueue<>((Comparator.comparingInt(edge -> edge.weight)));
63+
PriorityQueue<Edge> edges = new PriorityQueue<>(Comparator.comparingInt(edge -> edge.weight));
7864
for (int i = 0; i < nodes; i++) {
7965
minGraph[i] = new HashSet<>();
8066
connectedGroups[i] = new HashSet<>();
@@ -83,18 +69,21 @@ public HashSet<Edge>[] kruskal(HashSet<Edge>[] graph) {
8369
edges.addAll(graph[i]);
8470
}
8571
int connectedElements = 0;
86-
// as soon as two sets merge all the elements, the algorithm must stop
8772
while (connectedElements != nodes && !edges.isEmpty()) {
8873
Edge edge = edges.poll();
89-
// This if avoids cycles
74+
75+
// Avoid forming cycles by checking if the nodes belong to different connected components
9076
if (!connectedGroups[captain[edge.from]].contains(edge.to) && !connectedGroups[captain[edge.to]].contains(edge.from)) {
91-
// merge sets of the captains of each point connected by the edge
77+
// Merge the two sets of nodes connected by the edge
9278
connectedGroups[captain[edge.from]].addAll(connectedGroups[captain[edge.to]]);
93-
// update captains of the elements merged
79+
80+
// Update the captain for each merged node
9481
connectedGroups[captain[edge.from]].forEach(i -> captain[i] = captain[edge.from]);
95-
// add Edge to minimal graph
82+
83+
// Add the edge to the resulting MST graph
9684
addEdge(minGraph, edge.from, edge.to, edge.weight);
97-
// count how many elements have been merged
85+
86+
// Update the count of connected nodes
9887
connectedElements = connectedGroups[captain[edge.from]].size();
9988
}
10089
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package com.thealgorithms.datastructures.graphs;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import java.util.ArrayList;
7+
import java.util.Arrays;
8+
import java.util.HashSet;
9+
import java.util.List;
10+
import org.junit.jupiter.api.BeforeEach;
11+
import org.junit.jupiter.api.Test;
12+
13+
public class KruskalTest {
14+
15+
private Kruskal kruskal;
16+
private HashSet<Kruskal.Edge>[] graph;
17+
18+
@BeforeEach
19+
public void setUp() {
20+
kruskal = new Kruskal();
21+
int n = 7;
22+
graph = new HashSet[n];
23+
for (int i = 0; i < n; i++) {
24+
graph[i] = new HashSet<>();
25+
}
26+
27+
// Add edges to the graph
28+
Kruskal.addEdge(graph, 0, 1, 2);
29+
Kruskal.addEdge(graph, 0, 2, 3);
30+
Kruskal.addEdge(graph, 0, 3, 3);
31+
Kruskal.addEdge(graph, 1, 2, 4);
32+
Kruskal.addEdge(graph, 2, 3, 5);
33+
Kruskal.addEdge(graph, 1, 4, 3);
34+
Kruskal.addEdge(graph, 2, 4, 1);
35+
Kruskal.addEdge(graph, 3, 5, 7);
36+
Kruskal.addEdge(graph, 4, 5, 8);
37+
Kruskal.addEdge(graph, 5, 6, 9);
38+
}
39+
40+
@Test
41+
public void testKruskal() {
42+
int n = 6;
43+
HashSet<Kruskal.Edge>[] graph = new HashSet[n];
44+
45+
for (int i = 0; i < n; i++) {
46+
graph[i] = new HashSet<>();
47+
}
48+
49+
Kruskal.addEdge(graph, 0, 1, 4);
50+
Kruskal.addEdge(graph, 0, 2, 2);
51+
Kruskal.addEdge(graph, 1, 2, 1);
52+
Kruskal.addEdge(graph, 1, 3, 5);
53+
Kruskal.addEdge(graph, 2, 3, 8);
54+
Kruskal.addEdge(graph, 2, 4, 10);
55+
Kruskal.addEdge(graph, 3, 4, 2);
56+
Kruskal.addEdge(graph, 3, 5, 6);
57+
Kruskal.addEdge(graph, 4, 5, 3);
58+
59+
HashSet<Kruskal.Edge>[] result = kruskal.kruskal(graph);
60+
61+
List<List<Integer>> actualEdges = new ArrayList<>();
62+
for (HashSet<Kruskal.Edge> edges : result) {
63+
for (Kruskal.Edge edge : edges) {
64+
actualEdges.add(Arrays.asList(edge.from, edge.to, edge.weight));
65+
}
66+
}
67+
68+
List<List<Integer>> expectedEdges = Arrays.asList(Arrays.asList(1, 2, 1), Arrays.asList(0, 2, 2), Arrays.asList(3, 4, 2), Arrays.asList(4, 5, 3), Arrays.asList(1, 3, 5));
69+
70+
assertTrue(actualEdges.containsAll(expectedEdges) && expectedEdges.containsAll(actualEdges));
71+
}
72+
73+
@Test
74+
public void testEmptyGraph() {
75+
HashSet<Kruskal.Edge>[] emptyGraph = new HashSet[0];
76+
HashSet<Kruskal.Edge>[] result = kruskal.kruskal(emptyGraph);
77+
assertEquals(0, result.length);
78+
}
79+
80+
@Test
81+
public void testSingleNodeGraph() {
82+
HashSet<Kruskal.Edge>[] singleNodeGraph = new HashSet[1];
83+
singleNodeGraph[0] = new HashSet<>();
84+
HashSet<Kruskal.Edge>[] result = kruskal.kruskal(singleNodeGraph);
85+
assertTrue(result[0].isEmpty());
86+
}
87+
88+
@Test
89+
public void testGraphWithDisconnectedNodes() {
90+
int n = 5;
91+
HashSet<Kruskal.Edge>[] disconnectedGraph = new HashSet[n];
92+
for (int i = 0; i < n; i++) {
93+
disconnectedGraph[i] = new HashSet<>();
94+
}
95+
96+
Kruskal.addEdge(disconnectedGraph, 0, 1, 2);
97+
Kruskal.addEdge(disconnectedGraph, 2, 3, 4);
98+
99+
HashSet<Kruskal.Edge>[] result = kruskal.kruskal(disconnectedGraph);
100+
101+
List<List<Integer>> actualEdges = new ArrayList<>();
102+
for (HashSet<Kruskal.Edge> edges : result) {
103+
for (Kruskal.Edge edge : edges) {
104+
actualEdges.add(Arrays.asList(edge.from, edge.to, edge.weight));
105+
}
106+
}
107+
108+
List<List<Integer>> expectedEdges = Arrays.asList(Arrays.asList(0, 1, 2), Arrays.asList(2, 3, 4));
109+
110+
assertTrue(actualEdges.containsAll(expectedEdges) && expectedEdges.containsAll(actualEdges));
111+
}
112+
}

0 commit comments

Comments
 (0)