Enhance docs, add tests in KahnsAlgorithm (#5965)

This commit is contained in:
Hardik Pawar
2024-10-24 10:59:16 +05:30
committed by GitHub
parent 3a9a2c4160
commit 13be2501c2
3 changed files with 134 additions and 28 deletions

View File

@ -810,6 +810,7 @@
* [FordFulkersonTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/FordFulkersonTest.java) * [FordFulkersonTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/FordFulkersonTest.java)
* [HamiltonianCycleTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/HamiltonianCycleTest.java) * [HamiltonianCycleTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/HamiltonianCycleTest.java)
* [JohnsonsAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/JohnsonsAlgorithmTest.java) * [JohnsonsAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/JohnsonsAlgorithmTest.java)
* [KahnsAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java)
* [KosarajuTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/KosarajuTest.java) * [KosarajuTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/KosarajuTest.java)
* [TarjansAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithmTest.java) * [TarjansAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithmTest.java)
* [WelshPowellTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/WelshPowellTest.java) * [WelshPowellTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/WelshPowellTest.java)

View File

@ -9,96 +9,120 @@ import java.util.Queue;
import java.util.Set; import java.util.Set;
/** /**
* A class that represents the adjaceny list of a graph * A class representing the adjacency list of a directed graph. The adjacency list
* maintains a mapping of vertices to their adjacent vertices.
*
* @param <E> the type of vertices, extending Comparable to ensure that vertices
* can be compared
*/ */
class AdjacencyList<E extends Comparable<E>> { class AdjacencyList<E extends Comparable<E>> {
Map<E, ArrayList<E>> adj; Map<E, ArrayList<E>> adj;
/**
* Constructor to initialize the adjacency list.
*/
AdjacencyList() { AdjacencyList() {
adj = new LinkedHashMap<E, ArrayList<E>>(); adj = new LinkedHashMap<>();
} }
/** /**
* This function adds an Edge to the adjaceny list * Adds a directed edge from one vertex to another in the adjacency list.
* If the vertex does not exist, it will be added to the list.
* *
* @param from , the vertex the edge is from * @param from the starting vertex of the directed edge
* @param to, the vertex the edge is going to * @param to the destination vertex of the directed edge
*/ */
void addEdge(E from, E to) { void addEdge(E from, E to) {
try { if (!adj.containsKey(from)) {
adj.get(from).add(to); adj.put(from, new ArrayList<>());
} catch (Exception E) {
adj.put(from, new ArrayList<E>());
adj.get(from).add(to);
} }
adj.get(from).add(to);
if (!adj.containsKey(to)) { if (!adj.containsKey(to)) {
adj.put(to, new ArrayList<E>()); adj.put(to, new ArrayList<>());
} }
} }
/** /**
* @param v, A vertex in a graph * Retrieves the list of adjacent vertices for a given vertex.
* @return returns an ArrayList of all the adjacents of vertex v *
* @param v the vertex whose adjacent vertices are to be fetched
* @return an ArrayList of adjacent vertices for vertex v
*/ */
ArrayList<E> getAdjacents(E v) { ArrayList<E> getAdjacents(E v) {
return adj.get(v); return adj.get(v);
} }
/** /**
* @return returns a set of all vertices in the graph * Retrieves the set of all vertices present in the graph.
*
* @return a set containing all vertices in the graph
*/ */
Set<E> getVertices() { Set<E> getVertices() {
return adj.keySet(); return adj.keySet();
} }
} }
/**
* A class that performs topological sorting on a directed graph using Kahn's algorithm.
*
* @param <E> the type of vertices, extending Comparable to ensure that vertices
* can be compared
*/
class TopologicalSort<E extends Comparable<E>> { class TopologicalSort<E extends Comparable<E>> {
AdjacencyList<E> graph; AdjacencyList<E> graph;
Map<E, Integer> inDegree; Map<E, Integer> inDegree;
/**
* Constructor to initialize the topological sorting class with a given graph.
*
* @param graph the directed graph represented as an adjacency list
*/
TopologicalSort(AdjacencyList<E> graph) { TopologicalSort(AdjacencyList<E> graph) {
this.graph = graph; this.graph = graph;
} }
/** /**
* Calculates the in degree of all vertices * Calculates the in-degree of all vertices in the graph. The in-degree is
* the number of edges directed into a vertex.
*/ */
void calculateInDegree() { void calculateInDegree() {
inDegree = new HashMap<>(); inDegree = new HashMap<>();
for (E vertex : graph.getVertices()) { for (E vertex : graph.getVertices()) {
if (!inDegree.containsKey(vertex)) { inDegree.putIfAbsent(vertex, 0);
inDegree.put(vertex, 0);
}
for (E adjacent : graph.getAdjacents(vertex)) { for (E adjacent : graph.getAdjacents(vertex)) {
try { inDegree.put(adjacent, inDegree.getOrDefault(adjacent, 0) + 1);
inDegree.put(adjacent, inDegree.get(adjacent) + 1);
} catch (Exception e) {
inDegree.put(adjacent, 1);
}
} }
} }
} }
/** /**
* Returns an ArrayList with vertices arranged in topological order * Returns an ArrayList containing the vertices of the graph arranged in
* topological order. Topological sorting ensures that for any directed edge
* (u, v), vertex u appears before vertex v in the ordering.
*
* @return an ArrayList of vertices in topological order
* @throws IllegalStateException if the graph contains a cycle
*/ */
ArrayList<E> topSortOrder() { ArrayList<E> topSortOrder() {
calculateInDegree(); calculateInDegree();
Queue<E> q = new LinkedList<E>(); Queue<E> q = new LinkedList<>();
for (final var entry : inDegree.entrySet()) { for (var entry : inDegree.entrySet()) {
if (entry.getValue() == 0) { if (entry.getValue() == 0) {
q.add(entry.getKey()); q.add(entry.getKey());
} }
} }
ArrayList<E> answer = new ArrayList<>(); ArrayList<E> answer = new ArrayList<>();
int processedVertices = 0;
while (!q.isEmpty()) { while (!q.isEmpty()) {
E current = q.poll(); E current = q.poll();
answer.add(current); answer.add(current);
processedVertices++;
for (E adjacent : graph.getAdjacents(current)) { for (E adjacent : graph.getAdjacents(current)) {
inDegree.put(adjacent, inDegree.get(adjacent) - 1); inDegree.put(adjacent, inDegree.get(adjacent) - 1);
if (inDegree.get(adjacent) == 0) { if (inDegree.get(adjacent) == 0) {
@ -107,12 +131,16 @@ class TopologicalSort<E extends Comparable<E>> {
} }
} }
if (processedVertices != graph.getVertices().size()) {
throw new IllegalStateException("Graph contains a cycle, topological sort not possible");
}
return answer; return answer;
} }
} }
/** /**
* A driver class that sorts a given graph in topological order. * A driver class that sorts a given graph in topological order using Kahn's algorithm.
*/ */
public final class KahnsAlgorithm { public final class KahnsAlgorithm {
private KahnsAlgorithm() { private KahnsAlgorithm() {
@ -130,7 +158,7 @@ public final class KahnsAlgorithm {
TopologicalSort<String> topSort = new TopologicalSort<>(graph); TopologicalSort<String> topSort = new TopologicalSort<>(graph);
// Printing the order // Printing the topological order
for (String s : topSort.topSortOrder()) { for (String s : topSort.topSortOrder()) {
System.out.print(s + " "); System.out.print(s + " ");
} }

View File

@ -0,0 +1,77 @@
package com.thealgorithms.datastructures.graphs;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.ArrayList;
import org.junit.jupiter.api.Test;
class KahnsAlgorithmTest {
@Test
void testBasicGraph() {
// Test case with a basic directed acyclic graph (DAG)
AdjacencyList<String> graph = new AdjacencyList<>();
graph.addEdge("a", "b");
graph.addEdge("c", "a");
graph.addEdge("a", "d");
graph.addEdge("b", "d");
TopologicalSort<String> topSort = new TopologicalSort<>(graph);
ArrayList<String> result = topSort.topSortOrder();
String[] expectedOrder = {"c", "a", "b", "d"};
assertArrayEquals(expectedOrder, result.toArray());
}
@Test
void testGraphWithMultipleSources() {
// Test case where graph has multiple independent sources
AdjacencyList<String> graph = new AdjacencyList<>();
graph.addEdge("a", "c");
graph.addEdge("b", "c");
TopologicalSort<String> topSort = new TopologicalSort<>(graph);
ArrayList<String> result = topSort.topSortOrder();
String[] expectedOrder = {"a", "b", "c"};
assertArrayEquals(expectedOrder, result.toArray());
}
@Test
void testDisconnectedGraph() {
// Test case for disconnected graph
AdjacencyList<String> graph = new AdjacencyList<>();
graph.addEdge("a", "b");
graph.addEdge("c", "d");
TopologicalSort<String> topSort = new TopologicalSort<>(graph);
ArrayList<String> result = topSort.topSortOrder();
String[] expectedOrder = {"a", "c", "b", "d"};
assertArrayEquals(expectedOrder, result.toArray());
}
@Test
void testGraphWithCycle() {
// Test case for a graph with a cycle - topological sorting is not possible
AdjacencyList<String> graph = new AdjacencyList<>();
graph.addEdge("a", "b");
graph.addEdge("b", "c");
graph.addEdge("c", "a");
TopologicalSort<String> topSort = new TopologicalSort<>(graph);
assertThrows(IllegalStateException.class, () -> topSort.topSortOrder());
}
@Test
void testSingleNodeGraph() {
AdjacencyList<String> graph = new AdjacencyList<>();
graph.addEdge("a", "a"); // self-loop
TopologicalSort<String> topSort = new TopologicalSort<>(graph);
assertThrows(IllegalStateException.class, () -> topSort.topSortOrder());
}
}