Skip to content

Commit

Permalink
feat/Workflow describe with start node reaches finish nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
brunolnetto committed Feb 16, 2022
1 parent 9aa9be4 commit 249890d
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 58 deletions.
2 changes: 1 addition & 1 deletion algorithms/eulerian-path/__test__/eulerianPath.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe('eulerianPath', () => {
.addEdges([edgeAB, edgeAC, edgeBC, edgeBD, edgeCE]);

eulerianPath(graph);

expect(console.warn).toHaveBeenCalledTimes(1);
});

Expand Down
32 changes: 11 additions & 21 deletions algorithms/eulerian-path/eulerianPath.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ import graphBridges from '../bridges/graphBridges.js';
export default function eulerianPath(graph) {
const eulerianPathVertices = [];

let forward_star=graph.getAdjacencyList(0);
let reverse_star=graph.getAdjacencyList(1);

let vertices_keys_to_indices=graph.getVerticesKeystoIndices();

// Set that contains all vertices with even rank (number of neighbors).
const evenRankVertices = {};

Expand All @@ -28,28 +23,24 @@ export default function eulerianPath(graph) {

// Detect whether graph contains Eulerian Circuit or Eulerian Path or none of them.
/** @params {GraphVertex} vertex */
let vertex_index=-1;
let vertex_key=''
let neighbours=[]

graph.getAllVertices().forEach((vertex) => {
vertex_key=vertex.getKey()
vertex_index=vertices_keys_to_indices[vertex_key];

neighbours=[...(new Set(forward_star[vertex_index].concat(reverse_star[vertex_index])))]

if (neighbours.length % 2) {
oddRankVertices[vertex_key] = neighbours;
if (vertex.getDegree() % 2) {
oddRankVertices[vertex.getKey()] = vertex;
} else {
evenRankVertices[vertex_key] = vertex;
evenRankVertices[vertex.getKey()] = vertex;
}
});

// Check whether we're dealing with Eulerian Circuit or Eulerian Path only.
// Graph would be an Eulerian Circuit in case if all its vertices has even degree.
// If not all vertices have even degree then graph must contain only two odd-degree
// vertices in order to have Euler Path.
const isCircuit = graph.isEulerianCycle();
const isCircuit = !Object.values(oddRankVertices).length;

if (!isCircuit && Object.values(oddRankVertices).length !== 2) {
console.warn('Eulerian path must contain two odd-ranked vertices');
return []
}

// Pick start vertex for traversal.
let startVertex = null;
Expand All @@ -72,8 +63,7 @@ export default function eulerianPath(graph) {
eulerianPathVertices.push(currentVertex);

// Detect all bridges in graph.
// We need to do it in order to not delete bridges if there are other edges
// exists for deletion.
// We need to do it in order to not delete bridges if there are other edges exists for deletion.
const bridges = graphBridges(graph);

// Peek the next edge to delete from graph.
Expand Down Expand Up @@ -108,4 +98,4 @@ export default function eulerianPath(graph) {
}

return eulerianPathVertices;
}
}
106 changes: 97 additions & 9 deletions data-structures/graph/Graph.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Iter from 'es-iter';
import _ from 'lodash';

import Queue from '../queue/Queue.js'
import stronglyConnectedComponents from '../../algorithms/strongly-connected-components/stronglyConnectedComponents.js';
import eulerianPath from '../../algorithms/eulerian-path/eulerianPath.js';
import depthFirstSearch from '../../algorithms/depth-first-search/depthFirstSearch.js';
Expand Down Expand Up @@ -105,8 +106,8 @@ export default class Graph {
}

/**
* @return {Array[string]}
*/
* @return {Array[string]}
*/
getAllVerticesKeys() {
return Object.keys(this.vertices);
}
Expand All @@ -132,17 +133,26 @@ export default class Graph {
return Object.keys(this.edges);
}

/**
* @return {*[string]}
*/
getVerticesKeys() {
return Object.keys(this.vertices)
}

/**
* @return {GraphVertex[]}
*/
getVerticesByKeys(vertex_keys) {
return vertex_keys.map(
(vertex_key) => {
return this.vertices[vertex_key]
})
}

/**
* @return {GraphVertex[]}
*/
getVerticesByIndexes(vertex_indexes) {
let index_to_key=this.getVerticesIndicestoKeys()

Expand Down Expand Up @@ -790,7 +800,7 @@ export default class Graph {
*/
isEulerian(){
const adjList = this.getAdjacencyList();
const reverseStart = this.getAdjacencyList(1);
const reverse_star = this.getAdjacencyList(1);
const n_vertices = this.getNumVertices();

// Check if all non-zero degree vertices are connected
Expand All @@ -801,7 +811,7 @@ export default class Graph {

// Check if in degree and out degree of every vertex is same
for (let i = 0; i < n_vertices; i++){
if (adjList[i].length != reverseStart[i].length){
if (adjList[i].length != reverse_star[i].length){
return false;
}
}
Expand Down Expand Up @@ -858,7 +868,7 @@ export default class Graph {
// pre[v]: order in which dfs examines v
// low[v]: lowest preorder of any vertex connected to v
// parent[] --> Stores parent vertices in DFS tree
#bridgesUtil(u, v, preorder, low, counter, bridges) {
bridgesUtil(u, v, preorder, low, counter, bridges) {
const adjList = this.getAdjacencyList();

preorder[v] = counter++;
Expand All @@ -867,7 +877,7 @@ export default class Graph {
for (let i = 0; i < adjList[v].length; i += 1) {
let w = adjList[v][i];
if (preorder[w] === -1) {
this.#bridgesUtil(v, w, preorder, low, counter, bridges);
this.bridgesUtil(v, w, preorder, low, counter, bridges);

low[v] = Math.min(low[v], low[w]);

Expand All @@ -884,10 +894,10 @@ export default class Graph {

// DFS based function to find all bridges. It uses recursive function bridgeUtil()
bridges() {
const graph_ = this.isDirected ? this.retrieveUndirected() : _.cloneDeep(this);
// const graph_ = this.isDirected ? this.retrieveUndirected() : _.cloneDeep(this);

// Mark all the vertices as not visited
const n_vertices = graph_.getNumVertices();
const n_vertices = this.getNumVertices();
const bridges = [];
let counter = 0;

Expand All @@ -898,7 +908,7 @@ export default class Graph {

for (const v of Iter.range(n_vertices)) {
if (preorder[v] === -1) {
this.#bridgesUtil(v, v, preorder, low, counter, bridges);
this.bridgesUtil(v, v, preorder, low, counter, bridges);
}
}

Expand Down Expand Up @@ -1183,6 +1193,84 @@ export default class Graph {
return subgraph
}

// Returns count of not reachable nodes from
// vertex v.
// It uses recursive DFSUtil()
countNotReach(from_vertex_index){
let num_vertices=this.getNumVertices();

// Mark all the vertices as not visited
let visited = new Array(num_vertices);

for(let i = 0; i < num_vertices; i++)
visited[i] = false;

// Call the recursive helper function
// to print DFS traversal
this.DFSUtil(from_vertex_index, visited);

// Return count of not visited nodes
let count = 0;
for(let i = 0; i < num_vertices; i++){
if (visited[i] == false)
count++;
}

return count;
}

#reachUtil(src, visited){
let adjList = this.getAdjacencyList()

// Mark all the vertices as not visited
// Create a queue for BFS
// a = visited
let queue_ = new Queue()

queue_.enqueue(src)

// Assign Component Number
visited[src] = 1

// Vector to store all the reachable
// nodes from 'src'
let reachableNodes = []

while (queue_.length > 0){
// Dequeue a vertex from queue
let u = queue_.dequeue()
reachableNodes.push(u)

// Get all adjacent vertices of the dequeued vertex u.
// If a adjacent has not been visited, then mark it visited and enqueue it
for(let neighbour_id of adjList[u]){
// Assign Component Number to all the reachable nodes
if (visited[neighbour_id] == 0){
visited[neighbour_id] = 1
queue_.enqueue(neighbour_id)
}
}
}

return reachableNodes
}

reachableNodes(from_vertex_key){
let vertices_keys_to_indices = this.getVerticesKeystoIndices(from_vertex_key)
let from_vertex_id = vertices_keys_to_indices[from_vertex_key]

let num_vertices = this.getNumVertices()
let visited = []

for(let i=0; i<num_vertices; i = i + 1) {
visited[i]=0
}

// Get the number of nodes in the graph
// Map to store list of reachable Nodes for a given node.
return this.#reachUtil(from_vertex_id, visited)
}

#recurAcyclicPaths(from_index, to_index, is_visited, local_path_list, paths) {
const adj_list = this.getAdjacencyList();

Expand Down
32 changes: 32 additions & 0 deletions data-structures/graph/__test__/Graph.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,20 @@ describe('Graph', () => {
expect(graph.getEulerianPath()).toStrictEqual([0, 1, 2, 3, 4, 5]);
});

/*
it('should return vertices indexes', () => {
const vertexA = new GraphVertex('A');
const vertexB = new GraphVertex('B');
const edgeAB = new GraphEdge(vertexA, vertexB);
const graph = new Graph();
graph.addEdges([edgeAB]);
expect(graph.getVerticesByIndexes([0, 1])).toStrictEqual(['A', 'B']);
});*/

it('should return false for a non-eulerian directed graph', () => {
const vertexA = new GraphVertex('A');
const vertexB = new GraphVertex('B');
Expand Down Expand Up @@ -802,6 +816,24 @@ describe('Graph', () => {
expect(neighbors[1]).toEqual(vertexC);
});

it('should return reachable nodes from from_vertex_key', () => {
const graph = new Graph(true);

const vertexA = new GraphVertex('A');
const vertexB = new GraphVertex('B');
const vertexC = new GraphVertex('C');
const vertexD = new GraphVertex('D');

graph.addVertex(vertexD)

const edgeAB = new GraphEdge(vertexA, vertexB);
const edgeAC = new GraphEdge(vertexA, vertexC);

graph.addEdges([edgeAB, edgeAC])

expect(graph.reachableNodes('A')).toEqual([1, 2, 3]);
});

it('should return graph density', () => {
const graph = new Graph(true);

Expand Down
10 changes: 10 additions & 0 deletions data-structures/linked-list/LinkedList.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,26 @@ export default class LinkedList {
/**
* @param {Function} [comparatorFunction]
*/

#length;

constructor(comparatorFunction) {
/** @var LinkedListNode */
this.head = null;

/** @var LinkedListNode */
this.tail = null;

/** @int */
this.#length = 0;

this.compare = new Comparator(comparatorFunction);
}

get length() {
return this.toArray().length
}

/**
* @param {*} value
* @return {LinkedList}
Expand Down
7 changes: 7 additions & 0 deletions data-structures/queue/Queue.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import LinkedList from '../linked-list/LinkedList.js';

export default class Queue {
#length;

constructor() {
// We're going to implement Queue based on LinkedList since the two
// structures are quite similar. Namely, they both operate mostly on
// the elements at the beginning and the end. Compare enqueue/dequeue
// operations of Queue with append/deleteHead operations of LinkedList.
this.linkedList = new LinkedList();
this.#length=0;
}

get length() {
return this.linkedList.length
}

/**
* @return {boolean}
*/
Expand Down
Loading

0 comments on commit 249890d

Please sign in to comment.