Skip to content

Added depth first search algorithm #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

Merged
merged 2 commits into from
Jul 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/algorithms/graph/breadth-first-search/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
# Breadth-first search

TBD.
Breadth-first search (BFS) is an algorithm for traversing or searching tree or graph data structures. It starts at the tree root (or some arbitrary node of a graph, sometimes referred to as a 'search key'), and explores all of the neighbor nodes at the present depth prior to moving on to the nodes at the next depth level.

## Applications

* Shortest path - in an unweighted graph, the shortest path is the path with least number of edges. With Breadth First, we always reach a vertex from given source using the minimum number of edges
* Peer to peer networks - find all neighbor nodes
* Crawlers in search engines - build index, start from source page and follow all links
* Social networks - find people within a given distance 'k'
* Broadcasting in networks - find all nodes to be reached by a broadcasting packet
* Path finding - find if there is a path between two vertices

## Complexity

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function breadthFirstSearch(
const currentVertex = queue.dequeue();
config.enterVertex(currentVertex);

const neighbors = currentVertex.getNeighbors();
const neighbors = graph.getNeighbors(currentVertex);
for (const neighbor of neighbors) {
if (config.allowEnterVertex(neighbor)) {
queue.enqueue(neighbor);
Expand Down
20 changes: 20 additions & 0 deletions src/algorithms/graph/depth-first-search/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Breadth-first search

Depth-first search (DFS) is an algorithm for traversing or searching tree or graph data structures. The algorithm starts at the root node (selecting some arbitrary node as the root node in the case of a graph) and explores as far as possible along each branch before backtracking.

## Applications

* Topological sort
* Cycle detection
* Path finding
* Solving puzzles with the only solution (e.g. mazes)

## Complexity

**Time Complexity**: TBD.

**Space Complexity**: TBD.

## References

- [Youtube](https://www.youtube.com/watch?v=AfSk24UTFS8);
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {Graph} from '../../../../data-structures/graph/graph';
import {GraphVertex} from '../../../../data-structures/graph/graphVertex';
import {GraphEdge} from '../../../../data-structures/graph/graphEdge';
import {depthFirstSearch} from '../depthFirstSearch';

test('should perform BFS on graph', () => {
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');
const vertexE = new GraphVertex('E');
const vertexF = new GraphVertex('F');
const vertexG = new GraphVertex('G');
const vertexH = new GraphVertex('H');

const edgeAB = new GraphEdge(vertexA, vertexB);
const edgeBC = new GraphEdge(vertexB, vertexC);
const edgeCG = new GraphEdge(vertexC, vertexG);
const edgeAD = new GraphEdge(vertexA, vertexD);
const edgeAE = new GraphEdge(vertexA, vertexE);
const edgeEF = new GraphEdge(vertexE, vertexF);
const edgeDH = new GraphEdge(vertexD, vertexH);

graph
.addEdge(edgeAB)
.addEdge(edgeBC)
.addEdge(edgeCG)
.addEdge(edgeAD)
.addEdge(edgeAE)
.addEdge(edgeEF)
.addEdge(edgeDH);

expect(graph.toString()).toBe('A,B,C,G,D,E,F,H');

const enterVertexCallback = jest.fn();
const leaveVertexCallback = jest.fn();

// Traverse graphs without callbacks first.
depthFirstSearch(graph, vertexA);

// Traverse graph with enterVertex and leaveVertex callbacks.
depthFirstSearch(graph, vertexA, {
enterVertex: enterVertexCallback,
leaveVertex: leaveVertexCallback,
});

expect(enterVertexCallback).toHaveBeenCalledTimes(8);
expect(leaveVertexCallback).toHaveBeenCalledTimes(8);
});
80 changes: 80 additions & 0 deletions src/algorithms/graph/depth-first-search/depthFirstSearch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/* eslint-disable no-unused-vars */
import {Graph} from '../../../data-structures/graph/graph';
import {GraphVertex} from '../../../data-structures/graph/graphVertex';
import {GraphConfig} from '../../../data-structures/graph/graphConfig';

/**
*
*
* @export
* @param {Graph} graph
* @param {GraphVertex} startVertex
* @param {GraphConfig} config
*/
export function depthFirstSearch(
graph: Graph,
startVertex: GraphVertex,
config: GraphConfig = null,
): void {
if (!graph) {
return;
}

if (!graph.getVertex(startVertex)) {
return;
}

config = initConfig(config);
depthFirstSearchRecursive(graph, startVertex, config);
}

/**
*
*
* @param {Graph} graph
* @param {GraphVertex} currentVertex
* @param {GraphConfig} config
*/
function depthFirstSearchRecursive(
graph: Graph,
currentVertex: GraphVertex,
config: GraphConfig,
) {
config.enterVertex(currentVertex);

const neighbors = graph.getNeighbors(currentVertex);
for (const neighbor of neighbors) {
depthFirstSearch(graph, neighbor, config);
}

config.leaveVertex(currentVertex);
}

/**
* @param {GraphConfig} config
* @return {GraphConfig}
*/
function initConfig(config: GraphConfig): GraphConfig {
config = config || ({} as GraphConfig);
config.enterVertex = config.enterVertex || ((vertex: GraphVertex) => {});
config.leaveVertex = config.leaveVertex || ((vertex: GraphVertex) => {});
config.allowEnterVertex = config.allowEnterVertex || isFirstEnter();
return config;
}

/**
* @param {GraphVertex} vertex
* @return {boolean}
*/
function isFirstEnter(): (vertex: GraphVertex) => boolean {
const visited = {};

return (next: GraphVertex) => {
if (!visited[next.getKey()]) {
visited[next.getKey()] = true;
return true;
}

return false;
};
}