Skip to content

Directed Graph implementation with a minimum operation set #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
173 changes: 173 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
env:
es6: true
node: true
extends: 'eslint:recommended'
globals:
api: false
rules:
indent:
- error
- 2
- SwitchCase: 1
VariableDeclarator:
var: 2
let: 2
const: 3
MemberExpression: 1
linebreak-style:
- error
- unix
quotes:
- error
- single
semi:
- error
- always
eqeqeq:
- error
- always
no-loop-func:
- error
strict:
- error
- global
block-spacing:
- error
- always
brace-style:
- error
- 1tbs
- allowSingleLine: true
camelcase:
- error
comma-style:
- error
- last
comma-spacing:
- error
- before: false
after: true
eol-last:
- error
func-call-spacing:
- error
- never
key-spacing:
- error
- beforeColon: false
afterColon: true
mode: minimum
keyword-spacing:
- error
- before: true
after: true
overrides:
function:
after: false
max-len:
- error
- code: 80
ignoreUrls: true
max-nested-callbacks:
- error
- max: 7
new-cap:
- error
- newIsCap: true
capIsNew: true
properties: true
new-parens:
- error
no-lonely-if:
- error
no-trailing-spaces:
- error
no-unneeded-ternary:
- error
no-whitespace-before-property:
- error
object-curly-spacing:
- error
- always
operator-assignment:
- error
- always
operator-linebreak:
- error
- after
semi-spacing:
- error
- before: false
after: true
space-before-blocks:
- error
- always
space-before-function-paren:
- error
- never
space-in-parens:
- error
- never
space-infix-ops:
- error
space-unary-ops:
- error
- words: true
nonwords: false
overrides:
typeof: false
no-unreachable:
- error
no-global-assign:
- error
no-self-compare:
- error
no-unmodified-loop-condition:
- error
no-constant-condition:
- error
- checkLoops: false
no-console:
- off
no-useless-concat:
- error
no-useless-escape:
- error
no-shadow-restricted-names:
- error
no-use-before-define:
- error
- functions: false
arrow-body-style:
- error
- as-needed
arrow-spacing:
- error
no-confusing-arrow:
- error
- allowParens: true
no-useless-computed-key:
- error
no-useless-rename:
- error
no-var:
- error
object-shorthand:
- error
- always
prefer-arrow-callback:
- error
prefer-const:
- error
prefer-numeric-literals:
- error
prefer-rest-params:
- error
prefer-spread:
- error
rest-spread-spacing:
- error
- never
template-curly-spacing:
- error
- never
162 changes: 162 additions & 0 deletions JavaScript/1-directedGraph.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
'use strict';

class DirectedGraph {
constructor(vertexN) {
this._adjacency = []; // _adjacency[v] = adjacency array for vertex v
this._indegree = []; // _indegree[v] = indegree of vertex v
this._weight = []; // _weight[v][w] = weight of the edge (v; w)
if (vertexN === undefined) return this;
let i;
for (i = 0; i < vertexN; ++i) {
this.addVertex();
}
return this;
}
addVertex() {
this._adjacency.push([]);
this._weight.push([]);
const vertexIndex = this._adjacency.length - 1;
this._indegree[vertexIndex] = 0;
return this;
}
hasVertex(v) {
if (v < 0 || v >= this.size) {
return false;
}
return true;
}
get size() {
return this._adjacency.length;
}
connect(v1, v2, weight) {
if (!this.hasVertex(v1) || !this.hasVertex(v2)) {
return false;
}
this._adjacency[v1].push(v2);
this._weight[v1][v2] = weight;
++this._indegree[v2];
return true;
}
disconnect(v1, v2) {
if (!this.hasVertex(v1) || !this.hasVertex(v2)) {
return false;
}
const vertexIndex = this._adjacency[v1].indexOf(v2);
if (vertexIndex < 0) return false;
this._adjacency[v1].splice(vertexIndex, 1);
this._weight[v1][v2] = undefined;
--this._indegree[v2];
return true;
}
isConnected(v1, v2) {
if (!this.hasVertex(v1) || !this.hasVertex(v2)) {
return false;
}
const vertexIndex = this._adjacency[v1].indexOf(v2);
if (vertexIndex < 0) return false;
return true;
}
getWeight(v1, v2) {
return this._weight[v1][v2];
}
transpose() {
const size = this.size;
const transposedGraph = new DirectedGraph(size);
let v, w, weight;
for (v = 0; v < size; ++v) {
for (w of this._adjacency[v]) {
weight = this.getWeight(v, w);
transposedGraph.connect(w, v, weight);
}
}
return transposedGraph;
}
/**
* Computes shortest paths from a single source vertex
* to all of the other vertices (Bellman-Ford inplementation).
*
* @param from = the vertex
* @return distanceArr[v] = minimum distance to v
* parentArr[v] = parent vertex for v in the shortest path
*/
minimumDistance(from) {
if (!this.hasVertex(from)) {
return null;
}
const distance = new Array(this.size);
const parent = new Array(this.size);
let i;
for (i = 0; i < this.size; ++i) {
distance[i] = Infinity;
parent[i] = -1;
}
distance[from] = 0;
for (i = 0; i < this.size - 1; ++i) {
for (let v in this._adjacency) { // for each vertex v
for (let w of this._adjacency[v]) { // for each incident edge for v
if (distance[w] > distance[v] + this.getWeight(v, w)) {
distance[w] = distance[v] + this.getWeight(v, w);
parent[w] = v;
}
}
}
}
// if any distance[i] changes, the graph has negative cycles
for (let v in this._adjacency) {
for (let w of this._adjacency[v]) {
if (distance[w] > distance[v] + this.getWeight(v, w)) {
return null;
}
}
}
return { distanceArr: distance, parentArr: parent };
}
toposort() {
const grey = 1,
black = 2,
sorted = new Array(),
marked = new Array(this.size);
const dfs = (v) => {
if (marked[v] === grey) return 0;
if (marked[v] === black) return 1;
marked[v] = grey;
for (let w of this._adjacency[v]) {
if (!dfs(w)) return undefined;
}
marked[v] = black;
sorted.unshift(v);
return marked[v];
};
for (let v in this._adjacency) {
if (marked[v]) continue;
if (!dfs(v)) return null;
}
return sorted;
}
}

const myGraph = new DirectedGraph(4);
myGraph.connect(0, 1, 2);
myGraph.connect(0, 2, 3);
myGraph.connect(1, 2, -2);
myGraph.connect(1, 3, 2);
myGraph.connect(2, 3, 3);

const checkVertex = 0;
const checkEdge = [2, 3];
if (myGraph.hasVertex(checkVertex)) {
console.log('I have vertex ' + checkVertex);
} else {
console.log('I do not have a vertex ' + checkVertex + ' :(');
}
if (myGraph.isConnected(...checkEdge)) {
console.log(checkEdge[0] + ' is connected with ' + checkEdge[1]);
console.log('It has weight ' + myGraph.getWeight(...checkEdge));
}
console.log('Lets find the shortest paths for checkVertex!');
console.time('Belman-Ford');
const result = myGraph.minimumDistance(checkVertex);
console.timeEnd('Belman-Ford');
console.dir(result);

console.log('Toposort test: ' + myGraph.toposort());