Skip to content

Commit 1696707

Browse files
committed
feat: implement Topological Sort algorithm with tests
1 parent 0f9479c commit 1696707

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import {topologicalSort} from './topological-sort';
2+
3+
describe('topologicalSort', () => {
4+
test('should return a valid topological ordering for a simple DAG', () => {
5+
const graph = [[1], [2], [3], []];
6+
const result = topologicalSort(graph);
7+
expect(result).toEqual([0, 1, 2, 3]);
8+
});
9+
test('should return a valid topological ordering for a more complex DAG', () => {
10+
const graph = [[1, 2], [3], [3], []];
11+
const result = topologicalSort(graph);
12+
expect(result).not.toBeNull();
13+
if (result) {
14+
expect(result.indexOf(0)).toBeLessThan(result.indexOf(1));
15+
expect(result.indexOf(0)).toBeLessThan(result.indexOf(2));
16+
expect(result.indexOf(1)).toBeLessThan(result.indexOf(3));
17+
expect(result.indexOf(2)).toBeLessThan(result.indexOf(3));
18+
}
19+
});
20+
test('should return null for a graph with a cycle', () => {
21+
const graph = [[1], [2], [0]];
22+
const result = topologicalSort(graph);
23+
expect(result).toBeNull();
24+
});
25+
test('should return a valid ordering for a disconnected DAG', () => {
26+
const graph = [[1], [], [3], []];
27+
const result = topologicalSort(graph);
28+
expect(result).not.toBeNull();
29+
if (result) {
30+
expect(result.indexOf(0)).toBeLessThan(result.indexOf(1));
31+
expect(result.indexOf(2)).toBeLessThan(result.indexOf(3));
32+
}
33+
});
34+
35+
test('should handle a graph with a single vertex', () => {
36+
const graph = [[]];
37+
const result = topologicalSort(graph);
38+
expect(result).toEqual([0]);
39+
});
40+
41+
test('should handle an empty graph', () => {
42+
const graph: number[][] = [];
43+
const result = topologicalSort(graph);
44+
expect(result).toEqual([]);
45+
});
46+
test('should handle a graph with self-loops', () => {
47+
const graph = [[1], [1, 2], []];
48+
const result = topologicalSort(graph);
49+
expect(result).toBeNull();
50+
});
51+
test('should handle a graph with multiple cycles', () => {
52+
const graph = [[1], [2], [0], [4], [3]];
53+
const result = topologicalSort(graph);
54+
expect(result).toBeNull();
55+
});
56+
});
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* Implementation of Topological Sort algorithm.
3+
*
4+
* Topological Sort is an algorithm used to linearly order the vertices of a directed graph
5+
* such that for every directed edge (u, v), vertex u comes before vertex v in the ordering.
6+
*
7+
* This algorithm only works on Directed Acyclic Graphs (DAGs).
8+
* If the graph contains a cycle, no valid topological sort exists.
9+
*/
10+
11+
const VertexState = {
12+
UNVISITED: 0,
13+
VISITING: 1,
14+
VISITED: 2,
15+
} as const;
16+
17+
type VertexState = (typeof VertexState)[keyof typeof VertexState];
18+
19+
/**
20+
* Performs a topological sort on a directed graph represented as an adjacency list.
21+
*
22+
* @param graph - Adjacency list representation of the graph where graph[i] contains
23+
* the vertices that vertex i has edges to.
24+
* @returns An array of vertices in topological order, or null if the graph contains a cycle.
25+
*/
26+
export function topologicalSort(graph: number[][]): number[] | null {
27+
const n = graph.length;
28+
const state: VertexState[] = Array(n).fill(VertexState.UNVISITED);
29+
const result: number[] = [];
30+
31+
const dfs = (vertex: number): boolean => {
32+
if (state[vertex] === VertexState.VISITING) {
33+
return false;
34+
}
35+
36+
// If vertex is already visited, no need to process it again
37+
if (state[vertex] === VertexState.VISITED) {
38+
return true;
39+
}
40+
41+
state[vertex] = VertexState.VISITING;
42+
43+
for (const neighbor of graph[vertex]) {
44+
if (!dfs(neighbor)) {
45+
return false;
46+
}
47+
}
48+
state[vertex] = VertexState.VISITED;
49+
result.unshift(vertex);
50+
return true;
51+
};
52+
53+
// Try to visit each unvisited vertex
54+
for (let i = 0; i < n; i++) {
55+
if (state[i] === VertexState.UNVISITED) {
56+
if (!dfs(i)) {
57+
return null;
58+
}
59+
}
60+
}
61+
return result;
62+
}

0 commit comments

Comments
 (0)