Simplest implementation of a Graph + Traversal algorithms.
make
# or, if you want to watch for changes
make watch
module.exports = class Graph { | |
constructor(V, E, directed = false) { | |
this.vertices = V; | |
this.edges = {}; | |
E.map(e => { | |
const a = e[0]; const b = e[1]; const w = e[2] === undefined ? 1 : e[2]; | |
this.addEdge(a, b, w); | |
if (!directed) this.addEdge(b, a, w); | |
}); | |
} | |
addEdge(va, vb, w) { | |
if (!(va in this.edges)) this.edges[va] = []; | |
this.edges[va].push({ node: vb, w }); | |
} | |
getEdges(v) { | |
return (v in this.edges) ? this.edges[v] : []; | |
} | |
getEdge(a, b) { | |
return (a in this.edges) ? this.edges[a].find(e => e.node === b) : undefined; | |
} | |
} |
const Graph = require('./Graph'); | |
const { dfs, dfsIterative, bfs } = require('./traversal'); | |
const g = new Graph(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'], [ | |
['a', 'b', 4], | |
['b', 'c', 8], | |
['c', 'd', 7], | |
['d', 'e', 9], | |
['e', 'f', 10], | |
['f', 'g', 2], | |
['g', 'h', 1], | |
['h', 'a', 4], | |
['b', 'h', 11], | |
['c', 'i', 2], | |
['c', 'f', 4], | |
['d', 'f', 14], | |
['h', 'i', 7], | |
]); | |
const logNode = function(vertex, previous, weight) { | |
console.log((previous ? previous : '∅') + ' -' + (weight ? '(' + weight + ')' : '') + '-> ' + vertex); | |
}; | |
console.log('Depth First Search, starting from a:'); | |
dfs(g, 'a', logNode); | |
console.log('\nDepth First Search Iterative, starting from a:'); | |
dfsIterative(g, 'a', logNode); | |
console.log('\nBreadth First Search, starting from a:'); | |
bfs(g, 'a', logNode); | |
console.log('\nDepth First Search, starting from i:'); | |
dfs(g, 'i', logNode); | |
console.log('\nBreadth First Search, starting from i:'); | |
bfs(g, 'i', logNode); | |
.DEFAULT_GOAL := run | |
SRC=$(wildcard *.js) | |
changed: $(SRC) | |
echo "Changed !" | |
exit 0 | |
run: $(SRC) | |
@node index.js | |
watch: | |
while true; do make -t -s -q changed || (echo "\n---"; make -s run); sleep 0.5; done |
module.exports = { | |
dfs, | |
dfsIterative, | |
bfs | |
}; | |
function dfs(graph, currentVertex, cbk, weight = undefined, visited = {}, previousVertex) { | |
visited[currentVertex] = true; | |
cbk(currentVertex, previousVertex, weight); | |
graph.getEdges(currentVertex).map(e => { | |
if (!visited[e.node]) { | |
dfs(graph, e.node, cbk, e.w, visited, currentVertex); | |
} | |
}); | |
} | |
function dfsIterative(graph, currentVertex, cbk) { | |
const stack = [{ v: currentVertex, prev: undefined, weight: undefined}]; | |
const visited = {}; | |
while(stack.length) { | |
const { v, prev, weight } = stack.pop(); | |
if (visited[v]) continue; | |
visited[v] = true; | |
cbk(v, prev, weight); | |
graph.getEdges(v).reverse().map(e => { // reverse: traversal in same order as recursive dfs (stack inverses order) | |
stack.push({ v: e.node, prev: v, weight: e.w }); | |
}); | |
} | |
} | |
function bfs(graph, currentVertex, cbk) { | |
const queue = [{ v: currentVertex, prev: undefined, weight: undefined}]; | |
const visited = {}; | |
while(queue.length) { | |
const { v, prev, weight } = queue.pop(); | |
if(visited[v]) continue; | |
visited[v] = true; | |
cbk(v, prev, weight); | |
graph.getEdges(v).map(e => { | |
if(!visited[e.node]) | |
queue.unshift({ v: e.node, prev: v, weight: e.w }); | |
}); | |
} | |
} |