Repository: TheAlgorithms/Java
Pull request #5965 (2024-10-24 05:00:39)

refactor: Enhance docs, add tests in KahnsAlgorithm
<!--
Thank you for your contribution!
In order to reduce the number of notifications sent to the maintainers, please:
- create your PR as draft, cf. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests,
- make sure that all of the CI checks pass,
- mark your PR as ready for review, cf. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request#marking-a-pull-request-as-ready-for-review
-->

<!-- For completed items, change [ ] to [x] -->

- [x] I have read [CONTRIBUTING.md](https://github.com/TheAlgorithms/Java/blob/master/CONTRIBUTING.md).
- [x] This pull request is all my own work -- I have not plagiarized it.
- [x] All filenames are in PascalCase.
- [x] All functions and variable names follow Java naming conventions.
- [x] All new algorithms have a URL in their comments that points to Wikipedia or other similar explanations.
- [x] All new code is formatted with `clang-format -i --style=file path/to/your/file.java`


Patch:
diff --git a/DIRECTORY.md b/DIRECTORY.md
index 4f427686..0539f0df 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)
diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java b/src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java
index d5035cf6..9a97bc3f 100644
--- a/src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java
+++ b/src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java
@@ -9,96 +9,120 @@ import java.util.Queue;
 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>> {
 
     Map<E, ArrayList<E>> adj;
 
+    /**
+     * Constructor to initialize the adjacency list.
+     */
     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 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<E>());
-            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<E>());
+            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<E> 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<E> getVertices() {
         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>> {
 
     AdjacencyList<E> graph;
     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) {
         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
+     * @throws IllegalStateException if the graph contains a cycle
      */
     ArrayList<E> topSortOrder() {
         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) {
                 q.add(entry.getKey());
             }
         }
 
         ArrayList<E> 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) {
@@ -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;
     }
 }
 
 /**
- * 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 +158,7 @@ public final class KahnsAlgorithm {
 
         TopologicalSort<String> topSort = new TopologicalSort<>(graph);
 
-        // Printing the order
+        // Printing the topological order
         for (String s : topSort.topSortOrder()) {
             System.out.print(s + " ");
         }


Test patch:
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 00000000..8d096a4b
--- /dev/null
+++ b/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java
@@ -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());
+    }
+}