Skip to content

Commit 95aeb96

Browse files
susanna josephsusanna joseph
authored andcommitted
feat: Added Kahn's Algorithm in Graphs
1 parent 08d8c6b commit 95aeb96

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

Graphs/Kahn.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
Source:
3+
https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm
4+
*/
5+
6+
/*
7+
Kahn's Algorithm for Topological Sorting
8+
----------------------------------------
9+
Works only on Directed Acyclic Graphs (DAGs).
10+
Idea:
11+
1. Compute indegree (number of incoming edges) for each node.
12+
2. Start with nodes having indegree = 0 (no dependencies).
13+
3. Repeatedly remove nodes with indegree = 0 and reduce indegree of their neighbors.
14+
4. If all nodes are processed, we have a valid topological order.
15+
5. If not, graph has a cycle.
16+
17+
Time Complexity: O(V + E) (V = vertices, E = edges)
18+
Space Complexity: O(V + E)
19+
*/
20+
21+
const KahnsAlgorithm = (numNodes, edges) => {
22+
// Input:
23+
// numNodes: number of vertices in the graph (0..numNodes-1)
24+
// edges: list of directed edges [u, v] meaning u -> v
25+
// Output:
26+
// topoOrder: array of vertices in topological order
27+
// OR empty array if graph contains a cycle
28+
29+
// Step 1: Build adjacency list and indegree array
30+
const adj = Array.from({ length: numNodes }, () => [])
31+
const indegree = Array(numNodes).fill(0)
32+
33+
for (const [u, v] of edges) {
34+
adj[u].push(v)
35+
indegree[v]++
36+
}
37+
38+
// Step 2: Initialize queue with all nodes having indegree = 0
39+
const queue = []
40+
for (let i = 0; i < numNodes; i++) {
41+
if (indegree[i] === 0) queue.push(i)
42+
}
43+
44+
const topoOrder = []
45+
46+
// Step 3: Process queue
47+
while (queue.length > 0) {
48+
const node = queue.shift()
49+
topoOrder.push(node)
50+
51+
for (const neighbor of adj[node]) {
52+
indegree[neighbor]--
53+
if (indegree[neighbor] === 0) {
54+
queue.push(neighbor)
55+
}
56+
}
57+
}
58+
59+
// Step 4: Verify if topological order includes all nodes
60+
return topoOrder.length === numNodes ? topoOrder : []
61+
}
62+
63+
export { KahnsAlgorithm }

Graphs/test/Kahn.test.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { KahnsAlgorithm } from '../Kahn.js'
2+
3+
// Check if a given order is a valid topological sort
4+
function isValidTopologicalOrder(order, numNodes, edges) {
5+
if (order.length !== numNodes) return false
6+
7+
const position = new Map()
8+
order.forEach((node, idx) => position.set(node, idx))
9+
10+
for (const [u, v] of edges) {
11+
// u must come before v in topo order
12+
if (position.get(u) > position.get(v)) return false
13+
}
14+
return true
15+
}
16+
17+
test('Test Case 1', () => {
18+
const numNodes = 6
19+
const edges = [
20+
[5, 2],
21+
[5, 0],
22+
[4, 0],
23+
[4, 1],
24+
[2, 3],
25+
[3, 1]
26+
]
27+
const topoOrder = KahnsAlgorithm(numNodes, edges)
28+
29+
expect(isValidTopologicalOrder(topoOrder, numNodes, edges)).toBe(true)
30+
})
31+
32+
test('Test Case 2', () => {
33+
const numNodes = 4
34+
const edges = [
35+
[0, 1],
36+
[1, 2],
37+
[2, 3]
38+
]
39+
const topoOrder = KahnsAlgorithm(numNodes, edges)
40+
41+
// Only one valid order exists
42+
expect(topoOrder).toStrictEqual([0, 1, 2, 3])
43+
})
44+
45+
test('Test Case 3 (Cycle Detection)', () => {
46+
const numNodes = 3
47+
const edges = [
48+
[0, 1],
49+
[1, 2],
50+
[2, 0]
51+
]
52+
const topoOrder = KahnsAlgorithm(numNodes, edges)
53+
54+
expect(topoOrder).toStrictEqual([])
55+
})

0 commit comments

Comments
 (0)