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
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,9 +348,11 @@
- 📄 [Point](src/main/java/com/thealgorithms/geometry/Point.java)
- 📁 **graph**
- 📄 [ConstrainedShortestPath](src/main/java/com/thealgorithms/graph/ConstrainedShortestPath.java)
- 📄 [DisjointSet](src/main/java/com/thealgorithms/graph/DisjointSet.java)
- 📄 [HopcroftKarp](src/main/java/com/thealgorithms/graph/HopcroftKarp.java)
- 📄 [PredecessorConstrainedDfs](src/main/java/com/thealgorithms/graph/PredecessorConstrainedDfs.java)
- 📄 [StronglyConnectedComponentOptimized](src/main/java/com/thealgorithms/graph/StronglyConnectedComponentOptimized.java)
- 📄 [TopologicalSort](src/main/java/com/thealgorithms/graph/TopologicalSort.java)
- 📄 [TravelingSalesman](src/main/java/com/thealgorithms/graph/TravelingSalesman.java)
- 📁 **greedyalgorithms**
- 📄 [ActivitySelection](src/main/java/com/thealgorithms/greedyalgorithms/ActivitySelection.java)
Expand Down
185 changes: 185 additions & 0 deletions src/main/java/com/thealgorithms/graph/DisjointSet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package com.thealgorithms.graph;

import java.util.ArrayList;
import java.util.List;

/**
* Implementation of the Disjoint Set (Union-Find) data structure with path
* compression
* and union by rank optimizations.
*
* <p>
* A disjoint-set data structure maintains a collection of disjoint dynamic
* sets.
* Each set is represented by a "representative" element. The data structure
* supports
* two main operations:
* </p>
* <ul>
* <li>Find: Determine which set an element belongs to by finding the
* representative</li>
* <li>Union: Join two sets into a single set</li>
* </ul>
*
* <p>
* Time Complexity:
* </p>
* <ul>
* <li>Find: O(α(n)) amortized</li>
* <li>Union: O(α(n)) amortized</li>
* </ul>
* <p>
* where α(n) is the inverse Ackermann function, which grows extremely slowly.
* For all practical values of n, α(n) ≤ 4.
* </p>
*
* <p>
* Space Complexity: O(n) where n is the number of elements
* </p>
*
* <p>
* Applications:
* </p>
* <ul>
* <li>Kruskal's algorithm for minimum spanning trees</li>
* <li>Finding connected components in graphs</li>
* <li>Online dynamic connectivity</li>
* <li>Least common ancestor in trees</li>
* </ul>
*/
public class DisjointSet {
private final int[] parent;
private final int[] rank;
private final int size;
private int numSets; // Tracks number of disjoint sets

/**
* Initializes a disjoint set data structure with elements from 0 to size-1.
*
* @param size number of elements
* @throws IllegalArgumentException if size is negative
*/
public DisjointSet(int size) {
if (size < 0) {
throw new IllegalArgumentException("Size must be non-negative");
}
this.size = size;
this.numSets = size;
parent = new int[size];
rank = new int[size];

for (int i = 0; i < size; i++) {
parent[i] = i;
rank[i] = 0;
}
}

/**
* Finds the representative of the set containing element x.
* Uses path compression to optimize future queries.
*
* @param x element to find representative for
* @return representative of x's set
* @throws IllegalArgumentException if x is out of bounds
*/
public int find(int x) {
if (x < 0 || x >= size) {
throw new IllegalArgumentException("Element out of bounds: " + x);
}

// Path compression: Make all nodes on path point directly to root
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}

/**
* Merges the sets containing elements x and y.
* Uses union by rank to keep the tree shallow.
*
* @param x first element
* @param y second element
* @return true if x and y were in different sets, false if they were already in
* same set
* @throws IllegalArgumentException if either element is out of bounds
*/
public boolean union(int x, int y) {
int rootX = find(x);
int rootY = find(y);

if (rootX == rootY) {
return false; // Already in same set
}

// Union by rank: Attach smaller rank tree under root of higher rank tree
if (rank[rootX] < rank[rootY]) {
parent[rootX] = rootY;
} else if (rank[rootX] > rank[rootY]) {
parent[rootY] = rootX;
} else {
parent[rootY] = rootX;
rank[rootX]++;
}

numSets--;
return true;
}

/**
* Checks if two elements are in the same set.
*
* @param x first element
* @param y second element
* @return true if x and y are in the same set
* @throws IllegalArgumentException if either element is out of bounds
*/
public boolean connected(int x, int y) {
return find(x) == find(y);
}

/**
* Returns the current number of disjoint sets.
*
* @return number of disjoint sets
*/
public int getNumSets() {
return numSets;
}

/**
* Returns the size of the set containing element x.
*
* @param x element to find set size for
* @return size of x's set
* @throws IllegalArgumentException if x is out of bounds
*/
public int getSetSize(int x) {
int root = find(x);
int count = 0;
for (int i = 0; i < size; i++) {
if (find(i) == root) {
count++;
}
}
return count;
}

/**
* Returns all elements in the same set as x.
*
* @param x element to find set members for
* @return list of all elements in x's set
* @throws IllegalArgumentException if x is out of bounds
*/
public List<Integer> getSetMembers(int x) {
int root = find(x);
List<Integer> members = new ArrayList<>();
for (int i = 0; i < size; i++) {
if (find(i) == root) {
members.add(i);
}
}
return members;
}
}
127 changes: 127 additions & 0 deletions src/main/java/com/thealgorithms/graph/TopologicalSort.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package com.thealgorithms.graph;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
* Implementation of Kahn's algorithm for topological sorting of a directed
* acyclic graph (DAG).
*
* <p>
* The algorithm finds a linear ordering of vertices such that for every
* directed edge u -> v,
* vertex u comes before v in the ordering. A topological ordering is possible
* if and only if the
* graph has no directed cycles (i.e., it is a DAG).
* </p>
*
* <p>
* Time Complexity: O(V + E) where V is the number of vertices and E is the
* number of edges
* </p>
* <p>
* Space Complexity: O(V) for the queue and in-degree array
* </p>
*
* <p>
* Applications:
* </p>
* <ul>
* <li>Task scheduling with dependencies</li>
* <li>Build system dependency resolution</li>
* <li>Course prerequisite planning</li>
* <li>Package dependency management</li>
* </ul>
*/
public final class TopologicalSort {

private TopologicalSort() {
}

/**
* Performs topological sorting using Kahn's algorithm.
*
* @param numVertices number of vertices in the graph (0 to numVertices-1)
* @param edges list of directed edges, where each edge is represented as
* int[]{from, to}
* @return an array containing the topologically sorted order, or null if a
* cycle exists
* @throws IllegalArgumentException if edges is null or contains invalid
* vertices
*/
public static int[] sort(int numVertices, Iterable<int[]> edges) {
if (edges == null) {
throw new IllegalArgumentException("Edge list must not be null");
}

// Create adjacency list representation
List<List<Integer>> graph = new ArrayList<>(numVertices);
for (int i = 0; i < numVertices; i++) {
graph.add(new ArrayList<>());
}

// Calculate in-degree for each vertex
int[] inDegree = new int[numVertices];
for (int[] edge : edges) {
if (edge[0] < 0 || edge[0] >= numVertices || edge[1] < 0 || edge[1] >= numVertices) {
throw new IllegalArgumentException("Invalid vertex in edge: " + Arrays.toString(edge));
}
graph.get(edge[0]).add(edge[1]);
inDegree[edge[1]]++;
}

// Initialize queue with vertices having no incoming edges
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < numVertices; i++) {
if (inDegree[i] == 0) {
queue.offer(i);
}
}

int[] result = new int[numVertices];
int index = 0;

// Process vertices in topological order
while (!queue.isEmpty()) {
int vertex = queue.poll();
result[index++] = vertex;

// Reduce in-degree of neighbors and add to queue if in-degree becomes 0
for (int neighbor : graph.get(vertex)) {
inDegree[neighbor]--;
if (inDegree[neighbor] == 0) {
queue.offer(neighbor);
}
}
}

// Check if topological sort is possible (no cycles)
return index == numVertices ? result : null;
}

/**
* Alternative version that returns the result as a List and throws an exception
* if a cycle is detected.
*
* @param numVertices number of vertices in the graph (0 to numVertices-1)
* @param edges list of directed edges, where each edge is represented as
* int[]{from, to}
* @return a list containing the vertices in topologically sorted order
* @throws IllegalArgumentException if the graph contains a cycle or if input is
* invalid
*/
public static List<Integer> sortAndDetectCycle(int numVertices, List<int[]> edges) {
int[] result = sort(numVertices, edges);
if (result == null) {
throw new IllegalArgumentException("Graph contains a cycle");
}
List<Integer> sorted = new ArrayList<>(numVertices);
for (int vertex : result) {
sorted.add(vertex);
}
return sorted;
}
}
Loading
Loading