Skip to content

Commit

Permalink
Add Topological Sort implementation (yangshun#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
yangshun authored Mar 12, 2018
1 parent f7767e3 commit 259803f
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Data Structures and Algorithms library for JavaScript. Pretty much still WIP but
- [Merge Sort](lib/algorithms/mergeSort.js)
- [Quicksort](lib/algorithms/quickSort.js)
- [Heap Sort](lib/algorithms/heapSort.js)
- Topological Sort (TODO)
- [Topological Sort](lib/algorithms/topologicalSort.js)
- [Breadth-first Search](lib/algorithms/breadthFirstSearch.js)
- [Depth-first Search](lib/algorithms/depthFirstSearch.js)
- Djikstra's Algorithm (TODO)
Expand Down
92 changes: 92 additions & 0 deletions lib/algorithms/__tests__/topologicalSort.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import topologicalSort from '../topologicalSort';

describe('topologicalSort', () => {
test('empty graph', () => {
expect(topologicalSort({})).toEqual([]);
});

test('graphs with one node', () => {
expect(topologicalSort({ '1': [] })).toEqual(['1']);
});

test('graphs with two nodes', () => {
expect(topologicalSort({ '1': ['2'], '2': [] })).toEqual(['1', '2']);
});

test('graphs with multiple nodes', () => {
expect(topologicalSort({ '1': ['2'], '2': ['3'], '3': [] })).toEqual([
'1',
'2',
'3',
]);
expect(topologicalSort({ '1': ['2', '3'], '2': [], '3': [] })).toEqual([
'1',
'2',
'3',
]);
expect(
topologicalSort({
'1': ['2', '3'],
'2': [],
'3': [],
'4': ['2'],
'5': ['3'],
}),
).toEqual(['1', '4', '5', '2', '3']);
expect(
topologicalSort({
'1': ['4', '5'],
'2': [],
'3': [],
'4': ['2'],
'5': ['3'],
}),
).toEqual(['1', '4', '5', '2', '3']);

expect(
topologicalSort({
'1': ['2', '3', '4', '5'],
'2': [],
'3': [],
'4': ['2'],
'5': ['3'],
}),
).toEqual(['1', '4', '5', '2', '3']);
// Graph taken from https://en.wikipedia.org/wiki/Topological_sorting
const graph = {
'2': [],
'3': ['8', '10'],
'5': ['11'],
'7': ['8', '11'],
'8': ['9'],
'9': [],
'10': [],
'11': ['2', '9'],
};
expect(topologicalSort(graph)).toEqual([
'3',
'5',
'7',
'10',
'8',
'11',
'2',
'9',
]);
});

test('graph with cycles', () => {
expect(topologicalSort({ '1': ['1'] })).toEqual([]);
expect(topologicalSort({ '1': ['1', '2'], '2': [] })).toEqual([]);
expect(topologicalSort({ '1': ['1', '2'], '2': ['1'] })).toEqual([]);
expect(
topologicalSort({
'1': ['4', '5'],
'2': ['1', '2', '3', '4', '5'],
'3': [],
'4': ['2'],
'5': ['3'],
}),
).toEqual([]);
});
});
43 changes: 43 additions & 0 deletions lib/algorithms/topologicalSort.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Queue } from '../';

/**
* Performs a topological sort on a directed graph.
* @param {Object} graph Node to array of traversable neighboring nodes.
* @return {number[]|string[]} A topological traversal of nodes.
*/
function topologicalSort(graph) {
const nodes = new Map();
const order = [];
const queue = new Queue();

Object.keys(graph).forEach(node => {
nodes.set(node, { in: 0, out: new Set(graph[node]) });
});

Object.keys(graph).forEach(node => {
graph[node].forEach(neighbor => {
nodes.get(neighbor).in += 1;
});
});

nodes.forEach((value, node) => {
if (value.in === 0) {
queue.enqueue(node);
}
});

while (queue.length) {
const node = queue.dequeue();
nodes.get(node).out.forEach(neighbor => {
nodes.get(neighbor).in -= 1;
if (nodes.get(neighbor).in === 0) {
queue.enqueue(neighbor);
}
});
order.push(node);
}

return order.length === Object.keys(graph).length ? order : [];
}

export default topologicalSort;
2 changes: 2 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import heapSort from './algorithms/heapSort';
import mergeSort from './algorithms/mergeSort';
import quickSelect from './algorithms/quickSelect';
import quickSort from './algorithms/quickSort';
import topologicalSort from './algorithms/topologicalSort';

// Data Structures
import Counter from './data-structures/Counter';
Expand All @@ -27,6 +28,7 @@ export {
mergeSort,
quickSelect,
quickSort,
topologicalSort,
// Data Structures
Counter,
Deque,
Expand Down

0 comments on commit 259803f

Please sign in to comment.