forked from TheAlgorithms/Java
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathKruskalAlgorithm.java
More file actions
167 lines (145 loc) · 5.21 KB
/
KruskalAlgorithm.java
File metadata and controls
167 lines (145 loc) · 5.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package com.thealgorithms.greedyalgorithms;
/**
* An encapsulated, self-contained implementation of Kruskal's algorithm
* for computing the Minimum Spanning Tree (MST) of a weighted, undirected graph.
* You can find more about this algorithm in the following link:
* <a href="https://www.geeksforgeeks.org/dsa/kruskals-minimum-spanning-tree-algorithm-greedy-algo-2/">Kruskal algorithm - Geeks for Geeks</a>
* <p>
* To avoid namespace conflicts and maintain isolation within larger projects,
* all collaborators (Edge, Graph, DisjointSet) are implemented as private
* static nested classes. This ensures no type leakage outside this file while
* preserving clean internal architecture.
* </p>
*
* <h2>Usage</h2>
* <pre>
* KruskalAlgorithm algo = new KruskalAlgorithm();
* KruskalAlgorithm.Graph graph = new KruskalAlgorithm.Graph(4);
* graph.addEdge(0,1,10);
* graph.addEdge(1,2,5);
* List<KruskalAlgorithm.Edge> mst = algo.computeMST(graph);
* </pre>
*
* <h2>Design Notes</h2>
* <ul>
* <li>Implements a fully isolated module without risk of polluting global scope.</li>
* <li>Inner classes preserve encapsulation but keep responsibilities separate.</li>
* <li>Algorithm complexity: O(e log e), dominated by edge sorting.</li>
* </ul>
*/
public class KruskalAlgorithm {
/**
* Computes the Minimum Spanning Tree (or Minimum Spanning Forest if the graph
* is disconnected) using Kruskal’s greedy strategy.
*
* @param graph the graph instance to process
* @return a list of edges forming the MST
*/
public java.util.List<Edge> computeMST(Graph graph) {
java.util.List<Edge> mst = new java.util.ArrayList<>();
java.util.List<Edge> edges = new java.util.ArrayList<>(graph.edges);
// Sort edges by ascending weight
java.util.Collections.sort(edges);
DisjointSet ds = new DisjointSet(graph.numberOfVertices);
for (Edge e : edges) {
int rootA = ds.find(e.source);
int rootB = ds.find(e.target);
if (rootA != rootB) {
mst.add(e);
ds.union(rootA, rootB);
if (mst.size() == graph.numberOfVertices - 1) {
break;
}
}
}
return mst;
}
/**
* Represents an immutable weighted edge between two vertices.
*/
public static final class Edge implements Comparable<Edge> {
private final int source;
private final int target;
private final int weight;
public Edge(int source, int target, int weight) {
if (weight < 0) {
throw new IllegalArgumentException("Weight cannot be negative.");
}
this.source = source;
this.target = target;
this.weight = weight;
}
public int getSource() {
return source;
}
public int getTarget() {
return target;
}
public int getWeight() {
return weight;
}
@Override
public int compareTo(Edge o) {
return Integer.compare(this.weight, o.weight);
}
}
/**
* Lightweight graph representation consisting solely of vertices and edges.
* All algorithmic behavior is delegated to higher-level components.
*/
public static final class Graph {
private final int numberOfVertices;
private final java.util.List<Edge> edges = new java.util.ArrayList<>();
public Graph(int numberOfVertices) {
if (numberOfVertices <= 0) {
throw new IllegalArgumentException("Graph must have at least one vertex.");
}
this.numberOfVertices = numberOfVertices;
}
/**
* Adds an undirected edge to the graph.
*/
public void addEdge(int source, int target, int weight) {
if (source < 0 || source >= numberOfVertices || target < 0 || target >= numberOfVertices) {
throw new IndexOutOfBoundsException("Vertex index out of range.");
}
edges.add(new Edge(source, target, weight));
}
}
/**
* Disjoint Set Union data structure supporting path compression
* and union-by-rank — essential for cycle detection in Kruskal's algorithm.
*/
private static final class DisjointSet {
private final int[] parent;
private final int[] rank;
DisjointSet(int size) {
parent = new int[size];
rank = new int[size];
for (int i = 0; i < size; i++) {
parent[i] = i;
}
}
public int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]); // Path compression
}
return parent[x];
}
public void union(int a, int b) {
int ra = find(a);
int rb = find(b);
if (ra == rb) {
return;
}
if (rank[ra] < rank[rb]) {
parent[ra] = rb;
} else if (rank[ra] > rank[rb]) {
parent[rb] = ra;
} else {
parent[rb] = ra;
rank[ra]++;
}
}
}
}