| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -71,7 +71,7 @@ | |||
| 71 | 71 | * [Detect Cycle](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/detect-cycle) - for both directed and undirected graphs (DFS and Disjoint Set based versions) | |
| 72 | 72 | * [Prim’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST) for weighted undirected graph | |
| 73 | 73 | * [Kruskal’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/kruskal) - finding Minimum Spanning Tree (MST) for weighted undirected graph | |
| 74 | - * Topological Sorting | ||
| 74 | + * [Topological Sorting](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/topological-sorting) - DFS method | ||
| 75 | 75 | * Eulerian path, Eulerian circuit | |
| 76 | 76 | * Strongly Connected Component algorithm | |
| 77 | 77 | * Shortest Path Faster Algorithm (SPFA) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,55 @@ | |||
| 1 | + # Topological Sorting | ||
| 2 | + | ||
| 3 | + In the field of computer science, a topological sort or | ||
| 4 | + topological ordering of a directed graph is a linear ordering | ||
| 5 | + of its vertices such that for every directed edge `uv` from | ||
| 6 | + vertex `u` to vertex `v`, `u` comes before `v` in the ordering. | ||
| 7 | + | ||
| 8 | + For instance, the vertices of the graph may represent tasks to | ||
| 9 | + be performed, and the edges may represent constraints that one | ||
| 10 | + task must be performed before another; in this application, a | ||
| 11 | + topological ordering is just a valid sequence for the tasks. | ||
| 12 | + | ||
| 13 | + A topological ordering is possible if and only if the graph has | ||
| 14 | + no directed cycles, that is, if it is a [directed acyclic graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph) | ||
| 15 | + (DAG). Any DAG has at least one topological ordering, and algorithms are | ||
| 16 | + known for constructing a topological ordering of any DAG in linear time. | ||
| 17 | + | ||
| 18 | +  | ||
| 19 | + | ||
| 20 | + A topological ordering of a directed acyclic graph: every edge goes from | ||
| 21 | + earlier in the ordering (upper left) to later in the ordering (lower right). | ||
| 22 | + A directed graph is acyclic if and only if it has a topological ordering. | ||
| 23 | + | ||
| 24 | + ## Example | ||
| 25 | + | ||
| 26 | +  | ||
| 27 | + | ||
| 28 | + The graph shown above has many valid topological sorts, including: | ||
| 29 | + | ||
| 30 | + - `5, 7, 3, 11, 8, 2, 9, 10` (visual left-to-right, top-to-bottom) | ||
| 31 | + - `3, 5, 7, 8, 11, 2, 9, 10` (smallest-numbered available vertex first) | ||
| 32 | + - `5, 7, 3, 8, 11, 10, 9, 2` (fewest edges first) | ||
| 33 | + - `7, 5, 11, 3, 10, 8, 9, 2` (largest-numbered available vertex first) | ||
| 34 | + - `5, 7, 11, 2, 3, 8, 9, 10` (attempting top-to-bottom, left-to-right) | ||
| 35 | + - `3, 7, 8, 5, 11, 10, 2, 9` (arbitrary) | ||
| 36 | + | ||
| 37 | + ## Application | ||
| 38 | + | ||
| 39 | + The canonical application of topological sorting is in | ||
| 40 | + **scheduling a sequence of jobs** or tasks based on their dependencies. The jobs | ||
| 41 | + are represented by vertices, and there is an edge from `x` to `y` if | ||
| 42 | + job `x` must be completed before job `y` can be started (for | ||
| 43 | + example, when washing clothes, the washing machine must finish | ||
| 44 | + before we put the clothes in the dryer). Then, a topological sort | ||
| 45 | + gives an order in which to perform the jobs. | ||
| 46 | + | ||
| 47 | + Other application is **dependency resolution**. Each vertex is a package | ||
| 48 | + and each edge is a dependency of package `a` on package 'b'. Then topological | ||
| 49 | + sorting will provide a sequence of installing dependencies in a way that every | ||
| 50 | + next dependency has its dependent packages to be installed in prior. | ||
| 51 | + | ||
| 52 | + ## References | ||
| 53 | + | ||
| 54 | + - [Wikipedia](https://en.wikipedia.org/wiki/Topological_sorting) | ||
| 55 | + - [Topological Sorting on YouTube by Tushar Roy](https://www.youtube.com/watch?v=ddTC4Zovtbc) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,53 @@ | |||
| 1 | + import GraphVertex from '../../../../data-structures/graph/GraphVertex'; | ||
| 2 | + import GraphEdge from '../../../../data-structures/graph/GraphEdge'; | ||
| 3 | + import Graph from '../../../../data-structures/graph/Graph'; | ||
| 4 | + import topologicalSort from '../topologicalSort'; | ||
| 5 | + | ||
| 6 | + describe('topologicalSort', () => { | ||
| 7 | + it('should do topological sorting on graph', () => { | ||
| 8 | + const vertexA = new GraphVertex('A'); | ||
| 9 | + const vertexB = new GraphVertex('B'); | ||
| 10 | + const vertexC = new GraphVertex('C'); | ||
| 11 | + const vertexD = new GraphVertex('D'); | ||
| 12 | + const vertexE = new GraphVertex('E'); | ||
| 13 | + const vertexF = new GraphVertex('F'); | ||
| 14 | + const vertexG = new GraphVertex('G'); | ||
| 15 | + const vertexH = new GraphVertex('H'); | ||
| 16 | + | ||
| 17 | + const edgeAC = new GraphEdge(vertexA, vertexC); | ||
| 18 | + const edgeBC = new GraphEdge(vertexB, vertexC); | ||
| 19 | + const edgeBD = new GraphEdge(vertexB, vertexD); | ||
| 20 | + const edgeCE = new GraphEdge(vertexC, vertexE); | ||
| 21 | + const edgeDF = new GraphEdge(vertexD, vertexF); | ||
| 22 | + const edgeEF = new GraphEdge(vertexE, vertexF); | ||
| 23 | + const edgeEH = new GraphEdge(vertexE, vertexH); | ||
| 24 | + const edgeFG = new GraphEdge(vertexF, vertexG); | ||
| 25 | + | ||
| 26 | + const graph = new Graph(true); | ||
| 27 | + | ||
| 28 | + graph | ||
| 29 | + .addEdge(edgeAC) | ||
| 30 | + .addEdge(edgeBC) | ||
| 31 | + .addEdge(edgeBD) | ||
| 32 | + .addEdge(edgeCE) | ||
| 33 | + .addEdge(edgeDF) | ||
| 34 | + .addEdge(edgeEF) | ||
| 35 | + .addEdge(edgeEH) | ||
| 36 | + .addEdge(edgeFG); | ||
| 37 | + | ||
| 38 | + const sortedVertices = topologicalSort(graph); | ||
| 39 | + | ||
| 40 | + expect(sortedVertices).toBeDefined(); | ||
| 41 | + expect(sortedVertices.length).toBe(graph.getAllVertices().length); | ||
| 42 | + expect(sortedVertices).toEqual([ | ||
| 43 | + vertexB, | ||
| 44 | + vertexD, | ||
| 45 | + vertexA, | ||
| 46 | + vertexC, | ||
| 47 | + vertexE, | ||
| 48 | + vertexH, | ||
| 49 | + vertexF, | ||
| 50 | + vertexG, | ||
| 51 | + ]); | ||
| 52 | + }); | ||
| 53 | + }); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,47 @@ | |||
| 1 | + import Stack from '../../../data-structures/stack/Stack'; | ||
| 2 | + import depthFirstSearch from '../depth-first-search/depthFirstSearch'; | ||
| 3 | + | ||
| 4 | + /** | ||
| 5 | + * @param {Graph} graph | ||
| 6 | + */ | ||
| 7 | + export default function topologicalSort(graph) { | ||
| 8 | + // Create a set of all vertices we want to visit. | ||
| 9 | + const unvisitedSet = {}; | ||
| 10 | + graph.getAllVertices().forEach((vertex) => { | ||
| 11 | + unvisitedSet[vertex.getKey()] = vertex; | ||
| 12 | + }); | ||
| 13 | + | ||
| 14 | + // Create a set for all vertices that we've already visited. | ||
| 15 | + const visitedSet = {}; | ||
| 16 | + | ||
| 17 | + // Create a stack of already ordered vertices. | ||
| 18 | + const sortedStack = new Stack(); | ||
| 19 | + | ||
| 20 | + const dfsCallbacks = { | ||
| 21 | + enterVertex: ({ currentVertex }) => { | ||
| 22 | + // Add vertex to visited set in case if all its children has been explored. | ||
| 23 | + visitedSet[currentVertex.getKey()] = currentVertex; | ||
| 24 | + | ||
| 25 | + // Remove this vertex from unvisited set. | ||
| 26 | + delete unvisitedSet[currentVertex.getKey()]; | ||
| 27 | + }, | ||
| 28 | + leaveVertex: ({ currentVertex }) => { | ||
| 29 | + // If the vertex has been totally explored then we may push it to stack. | ||
| 30 | + sortedStack.push(currentVertex); | ||
| 31 | + }, | ||
| 32 | + allowTraversal: ({ nextVertex }) => { | ||
| 33 | + return !visitedSet[nextVertex.getKey()]; | ||
| 34 | + }, | ||
| 35 | + }; | ||
| 36 | + | ||
| 37 | + // Let's go and do DFS for all unvisited nodes. | ||
| 38 | + while (Object.keys(unvisitedSet).length) { | ||
| 39 | + const currentVertexKey = Object.keys(unvisitedSet)[0]; | ||
| 40 | + const currentVertex = unvisitedSet[currentVertexKey]; | ||
| 41 | + | ||
| 42 | + // Do DFS for current node. | ||
| 43 | + depthFirstSearch(graph, currentVertex, dfsCallbacks); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + return sortedStack.toArray(); | ||
| 47 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,13 +9,21 @@ export default class LinkedList { | |||
| 9 | 9 | this.tail = null; | |
| 10 | 10 | } | |
| 11 | 11 | ||
| 12 | + /** | ||
| 13 | + * @param {*} value | ||
| 14 | + * @return {LinkedList} | ||
| 15 | + */ | ||
| 12 | 16 | prepend(value) { | |
| 13 | 17 | // Make new node to be a head. | |
| 14 | 18 | this.head = new LinkedListNode(value, this.head); | |
| 15 | 19 | ||
| 16 | 20 | return this; | |
| 17 | 21 | } | |
| 18 | 22 | ||
| 23 | + /** | ||
| 24 | + * @param {*} value | ||
| 25 | + * @return {LinkedList} | ||
| 26 | + */ | ||
| 19 | 27 | append(value) { | |
| 20 | 28 | const newNode = new LinkedListNode(value); | |
| 21 | 29 | ||
@@ -34,6 +42,10 @@ export default class LinkedList { | |||
| 34 | 42 | return this; | |
| 35 | 43 | } | |
| 36 | 44 | ||
| 45 | + /** | ||
| 46 | + * @param {*} value | ||
| 47 | + * @return {LinkedListNode} | ||
| 48 | + */ | ||
| 37 | 49 | delete(value) { | |
| 38 | 50 | if (!this.head) { | |
| 39 | 51 | return null; | |
@@ -67,6 +79,12 @@ export default class LinkedList { | |||
| 67 | 79 | return deletedNode; | |
| 68 | 80 | } | |
| 69 | 81 | ||
| 82 | + /** | ||
| 83 | + * @param {Object} findParams | ||
| 84 | + * @param {*} findParams.value | ||
| 85 | + * @param {function} [findParams.callback] | ||
| 86 | + * @return {LinkedListNode} | ||
| 87 | + */ | ||
| 70 | 88 | find({ value = undefined, callback = undefined }) { | |
| 71 | 89 | if (!this.head) { | |
| 72 | 90 | return null; | |
@@ -91,6 +109,9 @@ export default class LinkedList { | |||
| 91 | 109 | return null; | |
| 92 | 110 | } | |
| 93 | 111 | ||
| 112 | + /** | ||
| 113 | + * @return {LinkedListNode} | ||
| 114 | + */ | ||
| 94 | 115 | deleteTail() { | |
| 95 | 116 | if (this.head === this.tail) { | |
| 96 | 117 | const deletedTail = this.tail; | |
@@ -116,6 +137,9 @@ export default class LinkedList { | |||
| 116 | 137 | return deletedTail; | |
| 117 | 138 | } | |
| 118 | 139 | ||
| 140 | + /** | ||
| 141 | + * @return {LinkedListNode} | ||
| 142 | + */ | ||
| 119 | 143 | deleteHead() { | |
| 120 | 144 | if (!this.head) { | |
| 121 | 145 | return null; | |
@@ -133,6 +157,9 @@ export default class LinkedList { | |||
| 133 | 157 | return deletedHead; | |
| 134 | 158 | } | |
| 135 | 159 | ||
| 160 | + /** | ||
| 161 | + * @return {LinkedListNode[]} | ||
| 162 | + */ | ||
| 136 | 163 | toArray() { | |
| 137 | 164 | const nodes = []; | |
| 138 | 165 | ||
@@ -145,6 +172,10 @@ export default class LinkedList { | |||
| 145 | 172 | return nodes; | |
| 146 | 173 | } | |
| 147 | 174 | ||
| 175 | + /** | ||
| 176 | + * @param {function} [callback] | ||
| 177 | + * @return {string} | ||
| 178 | + */ | ||
| 148 | 179 | toString(callback) { | |
| 149 | 180 | return this.toArray().map(node => node.toString(callback)).toString(); | |
| 150 | 181 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,10 +5,16 @@ export default class Stack { | |||
| 5 | 5 | this.linkedList = new LinkedList(); | |
| 6 | 6 | } | |
| 7 | 7 | ||
| 8 | + /** | ||
| 9 | + * @return {boolean} | ||
| 10 | + */ | ||
| 8 | 11 | isEmpty() { | |
| 9 | 12 | return !this.linkedList.tail; | |
| 10 | 13 | } | |
| 11 | 14 | ||
| 15 | + /** | ||
| 16 | + * @return {LinkedListNode} | ||
| 17 | + */ | ||
| 12 | 18 | peek() { | |
| 13 | 19 | if (!this.linkedList.tail) { | |
| 14 | 20 | return null; | |
@@ -17,15 +23,35 @@ export default class Stack { | |||
| 17 | 23 | return this.linkedList.tail.value; | |
| 18 | 24 | } | |
| 19 | 25 | ||
| 26 | + /** | ||
| 27 | + * @param {*} value | ||
| 28 | + */ | ||
| 20 | 29 | push(value) { | |
| 21 | 30 | this.linkedList.append(value); | |
| 22 | 31 | } | |
| 23 | 32 | ||
| 33 | + /** | ||
| 34 | + * @return {LinkedListNode} | ||
| 35 | + */ | ||
| 24 | 36 | pop() { | |
| 25 | 37 | const removedTail = this.linkedList.deleteTail(); | |
| 26 | 38 | return removedTail ? removedTail.value : null; | |
| 27 | 39 | } | |
| 28 | 40 | ||
| 41 | + /** | ||
| 42 | + * @return {*[]} | ||
| 43 | + */ | ||
| 44 | + toArray() { | ||
| 45 | + return this.linkedList | ||
| 46 | + .toArray() | ||
| 47 | + .map(linkedListNode => linkedListNode.value) | ||
| 48 | + .reverse(); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * @param {function} [callback] | ||
| 53 | + * @return {string} | ||
| 54 | + */ | ||
| 29 | 55 | toString(callback) { | |
| 30 | 56 | return this.linkedList.toString(callback); | |
| 31 | 57 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -62,4 +62,16 @@ describe('Stack', () => { | |||
| 62 | 62 | expect(stack.pop().value).toBe('test2'); | |
| 63 | 63 | expect(stack.pop().value).toBe('test1'); | |
| 64 | 64 | }); | |
| 65 | + | ||
| 66 | + it('should be possible to convert stack to array', () => { | ||
| 67 | + const stack = new Stack(); | ||
| 68 | + | ||
| 69 | + expect(stack.peek()).toBeNull(); | ||
| 70 | + | ||
| 71 | + stack.push(1); | ||
| 72 | + stack.push(2); | ||
| 73 | + stack.push(3); | ||
| 74 | + | ||
| 75 | + expect(stack.toArray()).toEqual([3, 2, 1]); | ||
| 76 | + }); | ||
| 65 | 77 | }); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments