From 832eeb95fa810acec10147aee055540ccfe4f2ba Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Thu, 24 Oct 2024 10:27:00 +0530 Subject: [PATCH 1/4] refactor: Enhance docs, add tests in `KahnsAlgorithm` --- .../datastructures/graphs/KahnsAlgorithm.java | 76 +++++++++++------- .../graphs/KahnsAlgorithmTest.java | 80 +++++++++++++++++++ 2 files changed, 128 insertions(+), 28 deletions(-) create mode 100644 src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java b/src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java index d5035cf625a6..82645fbb3d2c 100644 --- a/src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java +++ b/src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java @@ -9,86 +9,106 @@ 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 the type of vertices, extending Comparable to ensure that vertices + * can be compared */ class AdjacencyList> { Map> adj; + /** + * Constructor to initialize the adjacency list. + */ AdjacencyList() { - adj = new LinkedHashMap>(); + 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 to, the vertex the edge is going to + * @param from the starting vertex of the directed edge + * @param to the destination vertex of the directed edge */ void addEdge(E from, E to) { - try { - adj.get(from).add(to); - } catch (Exception E) { - adj.put(from, new ArrayList()); - adj.get(from).add(to); + if (!adj.containsKey(from)) { + adj.put(from, new ArrayList<>()); } + adj.get(from).add(to); if (!adj.containsKey(to)) { - adj.put(to, new ArrayList()); + adj.put(to, new ArrayList<>()); } } /** - * @param v, A vertex in a graph - * @return returns an ArrayList of all the adjacents of vertex v + * Retrieves the list of adjacent vertices for a given vertex. + * + * @param v the vertex whose adjacent vertices are to be fetched + * @return an ArrayList of adjacent vertices for vertex v */ ArrayList getAdjacents(E 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 getVertices() { return adj.keySet(); } } +/** + * A class that performs topological sorting on a directed graph using Kahn's algorithm. + * + * @param the type of vertices, extending Comparable to ensure that vertices + * can be compared + */ class TopologicalSort> { AdjacencyList graph; Map inDegree; + /** + * Constructor to initialize the topological sorting class with a given graph. + * + * @param graph the directed graph represented as an adjacency list + */ TopologicalSort(AdjacencyList 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() { inDegree = new HashMap<>(); for (E vertex : graph.getVertices()) { - if (!inDegree.containsKey(vertex)) { - inDegree.put(vertex, 0); - } + inDegree.putIfAbsent(vertex, 0); for (E adjacent : graph.getAdjacents(vertex)) { - try { - inDegree.put(adjacent, inDegree.get(adjacent) + 1); - } catch (Exception e) { - inDegree.put(adjacent, 1); - } + inDegree.put(adjacent, inDegree.getOrDefault(adjacent, 0) + 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 */ ArrayList topSortOrder() { calculateInDegree(); - Queue q = new LinkedList(); + Queue q = new LinkedList<>(); - for (final var entry : inDegree.entrySet()) { + for (var entry : inDegree.entrySet()) { if (entry.getValue() == 0) { q.add(entry.getKey()); } @@ -112,7 +132,7 @@ ArrayList topSortOrder() { } /** - * 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 { private KahnsAlgorithm() { @@ -130,7 +150,7 @@ public static void main(String[] args) { TopologicalSort topSort = new TopologicalSort<>(graph); - // Printing the order + // Printing the topological order for (String s : topSort.topSortOrder()) { System.out.print(s + " "); } diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java new file mode 100644 index 000000000000..97675a41ddaa --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java @@ -0,0 +1,80 @@ +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 graph = new AdjacencyList<>(); + graph.addEdge("a", "b"); + graph.addEdge("c", "a"); + graph.addEdge("a", "d"); + graph.addEdge("b", "d"); + + TopologicalSort topSort = new TopologicalSort<>(graph); + ArrayList 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 graph = new AdjacencyList<>(); + graph.addEdge("a", "c"); + graph.addEdge("b", "c"); + + TopologicalSort topSort = new TopologicalSort<>(graph); + ArrayList result = topSort.topSortOrder(); + + String[] expectedOrder = {"a", "b", "c"}; + assertArrayEquals(expectedOrder, result.toArray()); + } + + @Test + void testDisconnectedGraph() { + // Test case for disconnected graph + AdjacencyList graph = new AdjacencyList<>(); + graph.addEdge("a", "b"); + graph.addEdge("c", "d"); + + TopologicalSort topSort = new TopologicalSort<>(graph); + ArrayList 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 graph = new AdjacencyList<>(); + graph.addEdge("a", "b"); + graph.addEdge("b", "c"); + graph.addEdge("c", "a"); + + TopologicalSort topSort = new TopologicalSort<>(graph); + + assertThrows(IllegalStateException.class, () -> topSort.topSortOrder()); + } + + @Test + void testSingleNodeGraph() { + // Test case for a graph with a single node + AdjacencyList graph = new AdjacencyList<>(); + graph.addEdge("a", "a"); // self-loop + + TopologicalSort topSort = new TopologicalSort<>(graph); + ArrayList result = topSort.topSortOrder(); + + String[] expectedOrder = {}; + assertArrayEquals(expectedOrder, result.toArray()); + } +} From e37eb9853dd1e833a72315178233c9081fb9342a Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Thu, 24 Oct 2024 10:30:21 +0530 Subject: [PATCH 2/4] Fix --- .../datastructures/graphs/KahnsAlgorithm.java | 8 ++++++++ .../datastructures/graphs/KahnsAlgorithmTest.java | 4 +--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java b/src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java index 82645fbb3d2c..9a97bc3f4808 100644 --- a/src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java +++ b/src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java @@ -103,6 +103,7 @@ void calculateInDegree() { * (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 topSortOrder() { calculateInDegree(); @@ -115,10 +116,13 @@ ArrayList topSortOrder() { } ArrayList answer = new ArrayList<>(); + int processedVertices = 0; while (!q.isEmpty()) { E current = q.poll(); answer.add(current); + processedVertices++; + for (E adjacent : graph.getAdjacents(current)) { inDegree.put(adjacent, inDegree.get(adjacent) - 1); if (inDegree.get(adjacent) == 0) { @@ -127,6 +131,10 @@ ArrayList topSortOrder() { } } + if (processedVertices != graph.getVertices().size()) { + throw new IllegalStateException("Graph contains a cycle, topological sort not possible"); + } + return answer; } } diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java index 97675a41ddaa..3979b156fd04 100644 --- a/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java +++ b/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java @@ -72,9 +72,7 @@ void testSingleNodeGraph() { graph.addEdge("a", "a"); // self-loop TopologicalSort topSort = new TopologicalSort<>(graph); - ArrayList result = topSort.topSortOrder(); - String[] expectedOrder = {}; - assertArrayEquals(expectedOrder, result.toArray()); + assertThrows(IllegalStateException.class, () -> topSort.topSortOrder()); } } From 157d98ba3e61ebb3d827115e39a535f11e0aa2ca Mon Sep 17 00:00:00 2001 From: Hardvan Date: Thu, 24 Oct 2024 05:00:41 +0000 Subject: [PATCH 3/4] Update directory --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 4f4276860928..0539f0df1350 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -810,6 +810,7 @@ * [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) * [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) * [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) From 624bb1d42048b1e9c2e6f4915fd0f35e828e71e6 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Thu, 24 Oct 2024 10:38:56 +0530 Subject: [PATCH 4/4] Fix --- .../thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java index 3979b156fd04..8d096a4b4459 100644 --- a/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java +++ b/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java @@ -67,7 +67,6 @@ void testGraphWithCycle() { @Test void testSingleNodeGraph() { - // Test case for a graph with a single node AdjacencyList graph = new AdjacencyList<>(); graph.addEdge("a", "a"); // self-loop