diff --git a/src/algorithms/graph/topological-sorting/__test__/topologicalSortByCounting.test.js b/src/algorithms/graph/topological-sorting/__test__/topologicalSortByCounting.test.js new file mode 100644 index 00000000..830ff074 --- /dev/null +++ b/src/algorithms/graph/topological-sorting/__test__/topologicalSortByCounting.test.js @@ -0,0 +1,44 @@ +import GraphVertex from '../../../../data-structures/graph/GraphVertex'; +import GraphEdge from '../../../../data-structures/graph/GraphEdge'; +import Graph from '../../../../data-structures/graph/Graph'; +import topologicalSort from '../topologicalSortByCounting'; + +describe('topologicalSortByCounting', () => { + it('should do topological sorting on graph', () => { + const vertexNames = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']; + const edges = [ + { start: 'A', end: 'C' }, + { start: 'A', end: 'G' }, + { start: 'A', end: 'I' }, + { start: 'B', end: 'C' }, + { start: 'B', end: 'D' }, + { start: 'C', end: 'E' }, + { start: 'D', end: 'F' }, + { start: 'E', end: 'F' }, + { start: 'E', end: 'H' }, + { start: 'F', end: 'G' }, + ]; + const nameToVertex = {}; + const graph = new Graph(true); + + vertexNames.forEach((name) => { + const vertex = new GraphVertex(name); + nameToVertex[name] = vertex; + }); + edges.forEach((edge) => { + const startVertex = nameToVertex[edge.start]; + const endVertex = nameToVertex[edge.end]; + + graph.addEdge(new GraphEdge(startVertex, endVertex)); + }); + + const sortedVertices = topologicalSort(graph); + + expect(sortedVertices).toBeDefined(); + expect(sortedVertices.length).toBe(graph.getAllVertices().length); + graph.getAllEdges().forEach((edge) => { + expect(sortedVertices.indexOf(edge.startVertex)) + .toBeLessThan(sortedVertices.indexOf(edge.endVertex)); + }); + }); +}); diff --git a/src/algorithms/graph/topological-sorting/topologicalSortByCounting.js b/src/algorithms/graph/topological-sorting/topologicalSortByCounting.js new file mode 100644 index 00000000..17d0a2af --- /dev/null +++ b/src/algorithms/graph/topological-sorting/topologicalSortByCounting.js @@ -0,0 +1,45 @@ +import PriorityQueue from '../../../data-structures/priority-queue/PriorityQueue'; + +/** + * @param {Graph} graph + * Implements Khan's algorithms + */ +export default function topologicalSort(graph) { + // Count the preceders for all ndoes. + const precederCounts = {}; + + // Order nodes by number of preceders. + const priorityQueue = new PriorityQueue(); + + // The resulting sorted list. + const sortedStack = []; + + // Initialize the counter array. + graph.getAllVertices().forEach((vertex) => { + precederCounts[vertex.getKey()] = 0; + }); + + // Count preceders for all nodes. + graph.getAllEdges().forEach((edge) => { + precederCounts[edge.endVertex.getKey()] = precederCounts[edge.endVertex.getKey()] + 1; + }); + + graph.getAllVertices().forEach((vertex) => { + priorityQueue.add(vertex, precederCounts[vertex.getKey()]); + }); + + while (!priorityQueue.isEmpty()) { + const vertex = priorityQueue.poll(); + + sortedStack.push(vertex); + vertex.getEdges().forEach((edge) => { + // Decrease counter for all child nodes. + precederCounts[edge.endVertex.getKey()] = precederCounts[edge.endVertex.getKey()] - 1; + + // Update the information in priority queue. + priorityQueue.changePriority(edge.endVertex, precederCounts[edge.endVertex.getKey()]); + }); + } + + return sortedStack; +}