Skip to content

Commit 39d2365

Browse files
author
FSou1
committed
Add a topological sort algorithm, which is based on the dfs algorithm
1 parent fbc1bde commit 39d2365

File tree

5 files changed

+133
-2
lines changed

5 files changed

+133
-2
lines changed

src/algorithms/graph/depth-first-search/depthFirstSearch.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ function depthFirstSearchRecursive(
4444

4545
const neighbors = graph.getNeighbors(currentVertex);
4646
for (const neighbor of neighbors) {
47-
depthFirstSearch(graph, neighbor, config);
47+
if (config.allowEnterVertex(neighbor)) {
48+
depthFirstSearch(graph, neighbor, config);
49+
}
4850
}
4951

5052
config.leaveVertex(currentVertex);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Topological sort
2+
3+
A topological sort or topological ordering of a directed graph is a linear ordering of its vertices such that for every directed edge `uv` from vertex `u` to vertex `v`, `u` comes before `v` in the ordering.
4+
5+
## References
6+
7+
- [Youtube](https://youtu.be/AfSk24UTFS8?t=2508);
8+
- [Wikipedia](https://en.wikipedia.org/wiki/Topological_sorting);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import {topological} from '../topological';
2+
import {Graph} from '../../../../data-structures/graph/graph';
3+
import {GraphEdge} from '../../../../data-structures/graph/graphEdge';
4+
import {GraphVertex} from '../../../../data-structures/graph/graphVertex';
5+
6+
test('should perform topological on graph', () => {
7+
const vertexA = new GraphVertex('A');
8+
const vertexB = new GraphVertex('B');
9+
const vertexC = new GraphVertex('C');
10+
const vertexD = new GraphVertex('D');
11+
const vertexE = new GraphVertex('E');
12+
const vertexF = new GraphVertex('F');
13+
const vertexG = new GraphVertex('G');
14+
const vertexH = new GraphVertex('H');
15+
16+
const edgeAC = new GraphEdge(vertexA, vertexC);
17+
const edgeBC = new GraphEdge(vertexB, vertexC);
18+
const edgeBD = new GraphEdge(vertexB, vertexD);
19+
const edgeCE = new GraphEdge(vertexC, vertexE);
20+
const edgeDF = new GraphEdge(vertexD, vertexF);
21+
const edgeEF = new GraphEdge(vertexE, vertexF);
22+
const edgeEH = new GraphEdge(vertexE, vertexH);
23+
const edgeFG = new GraphEdge(vertexF, vertexG);
24+
25+
const graph = new Graph(true);
26+
27+
graph
28+
.addEdge(edgeAC)
29+
.addEdge(edgeBC)
30+
.addEdge(edgeBD)
31+
.addEdge(edgeCE)
32+
.addEdge(edgeDF)
33+
.addEdge(edgeEF)
34+
.addEdge(edgeEH)
35+
.addEdge(edgeFG);
36+
37+
const sortedVertices = topological(graph);
38+
39+
expect(sortedVertices).toBeDefined();
40+
expect(sortedVertices.length).toBe(graph.getVertices().length);
41+
expect(sortedVertices).toEqual([
42+
vertexB,
43+
vertexD,
44+
vertexA,
45+
vertexC,
46+
vertexE,
47+
vertexH,
48+
vertexF,
49+
vertexG,
50+
]);
51+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/* eslint-disable no-unused-vars */
2+
import {Graph} from '../../../data-structures/graph/graph';
3+
import {GraphVertex} from '../../../data-structures/graph/graphVertex';
4+
import {GraphConfig} from '../../../data-structures/graph/graphConfig';
5+
import {depthFirstSearch} from '../depth-first-search/depthFirstSearch';
6+
7+
/**
8+
*
9+
*
10+
* @export
11+
* @param {Graph} graph
12+
* @param {GraphConfig} config
13+
* @return {GraphVertex[]}
14+
*/
15+
export function topological(
16+
graph: Graph,
17+
config: GraphConfig = null,
18+
): GraphVertex[] {
19+
if (!graph) {
20+
return;
21+
}
22+
23+
// A set of unvisited vertices
24+
const unvisited = {};
25+
graph.getVertices().forEach((vertex) => {
26+
unvisited[vertex.getKey()] = vertex;
27+
});
28+
29+
// A set of visited vertices
30+
const visited = {};
31+
32+
// An array, which behaves like a stack (to hold the order)
33+
const stack = [];
34+
35+
config = config || ({} as GraphConfig);
36+
37+
// Visit only unvisited vertices
38+
config.allowEnterVertex =
39+
config.allowEnterVertex ||
40+
((vertex: GraphVertex) => {
41+
return !visited[vertex.getKey()];
42+
});
43+
44+
config.enterVertex =
45+
config.enterVertex ||
46+
((vertex: GraphVertex) => {
47+
// Remove a vertex from the unvisited set
48+
delete unvisited[vertex.getKey()];
49+
50+
// Mark a vertex as visited
51+
visited[vertex.getKey()] = vertex;
52+
});
53+
54+
/* Push the vertex onto the stack,
55+
* when all of the vertex neighbors have been visited */
56+
config.leaveVertex =
57+
config.leaveVertex ||
58+
((vertex: GraphVertex) => {
59+
// TODO: Replace with an actual stack implementation
60+
stack.push(vertex);
61+
});
62+
63+
while (Object.keys(unvisited).length) {
64+
const startVertexKey = Object.keys(unvisited)[0];
65+
const startVertex = unvisited[startVertexKey];
66+
67+
depthFirstSearch(graph, startVertex, config);
68+
}
69+
70+
return stack.reverse();
71+
}

src/data-structures/graph/graphVertex.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/* eslint-disable no-unused-vars */
22
import {GraphEdge} from './graphEdge';
3-
import {Graph} from './graph';
43

54
/**
65
*

0 commit comments

Comments
 (0)