Skip to content

Commit 3303eed

Browse files
committed
feat(graph): implement topological sort
1 parent b6f3ef1 commit 3303eed

File tree

3 files changed

+124
-0
lines changed

3 files changed

+124
-0
lines changed

algorithms/graph/depthFirstSearch.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
function initCallbacks(callbacks = {}) {
2+
const initiatedCallback = callbacks;
3+
4+
const stubCallback = () => {};
5+
6+
const allowTraversalCallback = (
7+
() => {
8+
const seen = {};
9+
return ({ nextVertex }) => {
10+
if (!seen[nextVertex.getKey()]) {
11+
seen[nextVertex.getKey()] = true;
12+
return true;
13+
}
14+
return false;
15+
};
16+
}
17+
)();
18+
19+
initiatedCallback.allowTraversal = callbacks.allowTraversal || allowTraversalCallback;
20+
initiatedCallback.enterVertex = callbacks.enterVertex || stubCallback;
21+
initiatedCallback.leaveVertex = callbacks.leaveVertex || stubCallback;
22+
23+
return initiatedCallback;
24+
}
25+
26+
function depthFirstSearchRecursive(graph, currentVertex, previousVertex, callbacks) {
27+
callbacks.enterVertex({ currentVertex, previousVertex });
28+
29+
graph.getNeighbors(currentVertex).forEach((nextVertex) => {
30+
if (callbacks.allowTraversal({ previousVertex, currentVertex, nextVertex })) {
31+
depthFirstSearchRecursive(graph, nextVertex, currentVertex, callbacks);
32+
}
33+
});
34+
35+
callbacks.leaveVertex({ currentVertex, previousVertex });
36+
}
37+
38+
module.exports = function depthFirstSearch(graph, startVertex, callbacks) {
39+
const previousVertex = null;
40+
depthFirstSearchRecursive(graph, startVertex, previousVertex, initCallbacks(callbacks));
41+
};

algorithms/graph/index.js

+39
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const dijkstra = require('./dijkstra');
55
const prim = require('./prim');
66
const kruskal = require('./kruskal');
77
const floydWarshall = require('./floydWarshall');
8+
const topologicalSort = require('./topologicalSort');
89

910
console.log('start Graph -------');
1011

@@ -179,4 +180,42 @@ console.log(`distances \n ${disFw.map((d) => `- ${d.join(', ')} - \n`).join(' ')
179180

180181
console.log('end floydWarshall --------');
181182

183+
console.log('start topologicalSort');
184+
185+
const tsVertexA = new GraphVertex('A');
186+
const tsVertexB = new GraphVertex('B');
187+
const tsVertexC = new GraphVertex('C');
188+
const tsVertexD = new GraphVertex('D');
189+
const tsVertexE = new GraphVertex('E');
190+
const tsVertexF = new GraphVertex('F');
191+
const tsVertexG = new GraphVertex('G');
192+
const tsVertexH = new GraphVertex('H');
193+
194+
const tsEdgeAC = new GraphEdge(tsVertexA, tsVertexC);
195+
const tsEdgeBC = new GraphEdge(tsVertexB, tsVertexC);
196+
const tsEdgeBD = new GraphEdge(tsVertexB, tsVertexD);
197+
const tsEdgeCE = new GraphEdge(tsVertexC, tsVertexE);
198+
const tsEdgeDF = new GraphEdge(tsVertexD, tsVertexF);
199+
const tsEdgeEF = new GraphEdge(tsVertexE, tsVertexF);
200+
const tsEdgeEH = new GraphEdge(tsVertexE, tsVertexH);
201+
const tsEdgeFG = new GraphEdge(tsVertexF, tsVertexG);
202+
203+
const graphForTS = new Graph(true);
204+
205+
graphForTS
206+
.addEdge(tsEdgeAC)
207+
.addEdge(tsEdgeBC)
208+
.addEdge(tsEdgeBD)
209+
.addEdge(tsEdgeCE)
210+
.addEdge(tsEdgeDF)
211+
.addEdge(tsEdgeEF)
212+
.addEdge(tsEdgeEH)
213+
.addEdge(tsEdgeFG);
214+
215+
const sortedVertices = topologicalSort(graphForTS);
216+
217+
console.log(`graph ${graphForTS.toString()} sortedVertices ${sortedVertices.toString()}`);
218+
219+
console.log('end topologicalSort');
220+
182221
module.exports = () => console.log('Graph is done');

algorithms/graph/topologicalSort.js

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const Stack = require('../../data-structures/stack/Stack');
2+
const depthFirstSearch = require('./depthFirstSearch');
3+
4+
module.exports = function topologicalSort(graph) {
5+
// Create a set of all vertices we want to visit.
6+
const unvisitedSet = {};
7+
graph.getAllVertices().forEach((vertex) => {
8+
unvisitedSet[vertex.getKey()] = vertex;
9+
});
10+
11+
// Create a set for all vertices that we've already visited.
12+
const visitedSet = {};
13+
14+
// Create a stack of already ordered vertices.
15+
const sortedStack = new Stack();
16+
17+
const dfsCallbacks = {
18+
enterVertex: ({ currentVertex }) => {
19+
// Add vertex to visited set in case if all its children has been explored.
20+
visitedSet[currentVertex.getKey()] = currentVertex;
21+
22+
// Remove this vertex from unvisited set.
23+
delete unvisitedSet[currentVertex.getKey()];
24+
},
25+
leaveVertex: ({ currentVertex }) => {
26+
// If the vertex has been totally explored then we may push it to stack.
27+
sortedStack.push(currentVertex);
28+
},
29+
allowTraversal: ({ nextVertex }) => {
30+
return !visitedSet[nextVertex.getKey()];
31+
},
32+
};
33+
34+
// Let's go and do DFS for all unvisited nodes.
35+
while (Object.keys(unvisitedSet).length) {
36+
const currentVertexKey = Object.keys(unvisitedSet)[0];
37+
const currentVertex = unvisitedSet[currentVertexKey];
38+
39+
// Do DFS for current node.
40+
depthFirstSearch(graph, currentVertex, dfsCallbacks);
41+
}
42+
43+
return sortedStack.toArray();
44+
};

0 commit comments

Comments
 (0)