Skip to content

refactor: Enhance docs, add tests in KahnsAlgorithm #5965

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 24, 2024
Merged
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
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,96 +9,120 @@
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) {
Expand All @@ -107,12 +131,16 @@ ArrayList<E> topSortOrder() {
}
}

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() {
Expand All @@ -130,7 +158,7 @@ public static void main(String[] args) {

TopologicalSort<String> topSort = new TopologicalSort<>(graph);

// Printing the order
// Printing the topological order
for (String s : topSort.topSortOrder()) {
System.out.print(s + " ");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}