Skip to content

Commit a083917

Browse files
author
FSou1
committed
Queue and Graph data structures + BFS algorithm were added
1 parent aa7be0a commit a083917

File tree

15 files changed

+543
-0
lines changed

15 files changed

+543
-0
lines changed

.editorconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
root = true
2+
3+
[*]
4+
indent_style = space
5+
indent_size = 2
6+
end_of_line = crlf
7+
charset = utf-8
8+
trim_trailing_whitespace = false
9+
insert_final_newline = false

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
# TypeScript Algorithms and Data structures
22
🔖 Algorithms and data structures implemented in TypeScript
33

4+
## Data structures
5+
6+
A data structure is a data organization, management, and storage format that enables efficient access and modification. More precisely, a data structure is a collection of data values, the relationships among them, and the functions or operations that can be applied to the data.
7+
8+
* [Queue](src/data-structures/queue) - a data structure to follow the FIFO principle;
9+
* [Graph](src/data-structures/graph) - a set of vertices and edges;
10+
411
## Algorithms
512

613
An algorithm is a finite sequence of well-defined, computer-implementable instructions, typically to solve a class of problems or to perform a computation.
@@ -18,6 +25,8 @@ An algorithm is a finite sequence of well-defined, computer-implementable instru
1825
* [Heapsort](/src/algorithms/sort/heapsort) - sort an array with a heapsort algorithm;
1926
* [Counting sort](/src/algorithms/sort/counting) - sort an array with a counting sort algorithm;
2027
* [Radix sort](/src/algorithms/sort/radix) - sort an array with a radix sort algorithm;
28+
* **Graph**
29+
* [Breadth-first search](/src/algorithms/graph/breadth-first-search) - traverse a graph, explore neighbor vertices first;
2130

2231
## How to use this repository
2332

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Breadth-first search
2+
3+
TBD.
4+
5+
## Complexity
6+
7+
**Time Complexity**: `O(|V| + |E|)` - since every vertex and every edge will be explored in the worst case, where |V| is the number of vertices and |E| is the number of edges in the graph;
8+
9+
**Space Complexity**: `O(|V|)` - this is in addition to the space required for the graph itself, which may vary depending on the graph representation;
10+
11+
## Referenes
12+
13+
- [Youtube](https://www.youtube.com/watch?v=s-CYnVz-uh4);
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import {Graph} from '../../../../data-structures/graph/graph';
2+
import {GraphVertex} from '../../../../data-structures/graph/graphVertex';
3+
import {GraphEdge} from '../../../../data-structures/graph/graphEdge';
4+
import {breadthFirstSearch} from '../breadthFirstSearch';
5+
6+
test('should perform BFS on graph', () => {
7+
const graph = new Graph(true);
8+
9+
const vertexA = new GraphVertex('A');
10+
const vertexB = new GraphVertex('B');
11+
const vertexC = new GraphVertex('C');
12+
const vertexD = new GraphVertex('D');
13+
const vertexE = new GraphVertex('E');
14+
const vertexF = new GraphVertex('F');
15+
const vertexG = new GraphVertex('G');
16+
const vertexH = new GraphVertex('H');
17+
18+
const edgeAB = new GraphEdge(vertexA, vertexB);
19+
const edgeBC = new GraphEdge(vertexB, vertexC);
20+
const edgeCG = new GraphEdge(vertexC, vertexG);
21+
const edgeAD = new GraphEdge(vertexA, vertexD);
22+
const edgeAE = new GraphEdge(vertexA, vertexE);
23+
const edgeEF = new GraphEdge(vertexE, vertexF);
24+
const edgeFD = new GraphEdge(vertexF, vertexD);
25+
const edgeDH = new GraphEdge(vertexD, vertexH);
26+
const edgeGH = new GraphEdge(vertexG, vertexH);
27+
28+
graph
29+
.addEdge(edgeAB)
30+
.addEdge(edgeBC)
31+
.addEdge(edgeCG)
32+
.addEdge(edgeAD)
33+
.addEdge(edgeAE)
34+
.addEdge(edgeEF)
35+
.addEdge(edgeFD)
36+
.addEdge(edgeDH)
37+
.addEdge(edgeGH);
38+
39+
expect(graph.toString()).toBe('A,B,C,G,D,E,F,H');
40+
41+
const enterVertexCallback = jest.fn();
42+
const leaveVertexCallback = jest.fn();
43+
44+
// Traverse graphs without callbacks first.
45+
breadthFirstSearch(graph, vertexA);
46+
47+
// Traverse graph with enterVertex and leaveVertex callbacks.
48+
breadthFirstSearch(graph, vertexA, {
49+
enterVertex: enterVertexCallback,
50+
leaveVertex: leaveVertexCallback,
51+
});
52+
53+
expect(enterVertexCallback).toHaveBeenCalledTimes(8);
54+
expect(leaveVertexCallback).toHaveBeenCalledTimes(8);
55+
});
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/* eslint-disable no-unused-vars */
2+
import {Graph} from '../../../data-structures/graph/graph';
3+
import {GraphVertex} from '../../../data-structures/graph/graphVertex';
4+
import {GraphConfig} from '../../../data-structures/graph/graphConfig';
5+
import {Queue} from '../../../data-structures/queue/queue';
6+
7+
/**
8+
*
9+
*
10+
* @export
11+
* @param {Graph} graph
12+
* @param {GraphVertex} startVertex
13+
* @param {GraphConfig} config
14+
*/
15+
export function breadthFirstSearch(
16+
graph: Graph,
17+
startVertex: GraphVertex,
18+
config: GraphConfig = null,
19+
): void {
20+
if (!graph) {
21+
return;
22+
}
23+
24+
if (!graph.getVertex(startVertex)) {
25+
return;
26+
}
27+
28+
config = initConfig(config);
29+
30+
const queue = new Queue<GraphVertex>();
31+
queue.enqueue(startVertex);
32+
33+
while (!queue.isEmpty()) {
34+
const currentVertex = queue.dequeue();
35+
config.enterVertex(currentVertex);
36+
37+
const neighbors = currentVertex.getNeighbors();
38+
for (const neighbor of neighbors) {
39+
if (config.allowEnterVertex(neighbor)) {
40+
queue.enqueue(neighbor);
41+
}
42+
}
43+
44+
config.leaveVertex(currentVertex);
45+
}
46+
}
47+
48+
/**
49+
* @param {GraphConfig} config
50+
* @return {GraphConfig}
51+
*/
52+
function initConfig(config: GraphConfig): GraphConfig {
53+
config = config || ({} as GraphConfig);
54+
config.enterVertex = config.enterVertex || ((vertex: GraphVertex) => {});
55+
config.leaveVertex = config.leaveVertex || ((vertex: GraphVertex) => {});
56+
config.allowEnterVertex = config.allowEnterVertex || isFirstEnter();
57+
return config;
58+
}
59+
60+
/**
61+
* @param {GraphVertex} vertex
62+
* @return {boolean}
63+
*/
64+
function isFirstEnter(): (vertex: GraphVertex) => boolean {
65+
const visited = {};
66+
67+
return ((next: GraphVertex) => {
68+
if (!visited[next.getKey()]) {
69+
visited[next.getKey()] = true;
70+
return true;
71+
}
72+
73+
return false;
74+
});
75+
}

src/data-structures/graph/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Graph
2+
3+
In mathematics, and more specifically in graph theory, a graph is a structure amounting to a set of objects in which some pairs of the objects are in some sense "related". The objects correspond to mathematical abstractions called vertices (also called nodes or points) and each of the related pairs of vertices is called an edge (also called link or line).
4+
5+
## Referenes
6+
7+
- [Youtube](https://www.youtube.com/watch?v=s-CYnVz-uh4);
8+
- [Wikipedia](https://en.wikipedia.org/wiki/Graph_(discrete_mathematics));
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import {Graph} from '../graph';
2+
import {GraphVertex} from '../graphVertex';
3+
4+
test('an empty graph returns valid string', () => {
5+
const graph = new Graph(false);
6+
expect(graph.toString()).toBe('');
7+
});
8+
9+
test('vertices can be added to graph', () => {
10+
const graph = new Graph(false);
11+
12+
const vertexA = new GraphVertex('A');
13+
const vertexB = new GraphVertex('B');
14+
15+
graph
16+
.addVertex(vertexA)
17+
.addVertex(vertexB);
18+
19+
expect(graph.toString()).toBe('A,B');
20+
});
21+
22+
test('existing vertex is returned', () => {
23+
const graph = new Graph(false);
24+
25+
const vertexA = new GraphVertex('A');
26+
const vertexB = new GraphVertex('B');
27+
28+
graph
29+
.addVertex(vertexA)
30+
.addVertex(vertexB);
31+
32+
expect(graph.getVertex(vertexA)).toBe(vertexA);
33+
expect(graph.getVertex(vertexB)).toBe(vertexB);
34+
});
35+
36+
test('missing vertex is returned as null', () => {
37+
const graph = new Graph(false);
38+
39+
const vertexA = new GraphVertex('A');
40+
const vertexB = new GraphVertex('B');
41+
42+
graph
43+
.addVertex(vertexA);
44+
45+
expect(graph.getVertex(vertexB)).toBe(null);
46+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {GraphVertex} from '../graphVertex';
2+
import {GraphEdge} from '../graphEdge';
3+
4+
test('edges are added, neighbors are valid', () => {
5+
const vertexA = new GraphVertex('A');
6+
const vertexB = new GraphVertex('B');
7+
const vertexC = new GraphVertex('C');
8+
9+
const edgeAB = new GraphEdge(vertexA, vertexB);
10+
const edgeAC = new GraphEdge(vertexA, vertexC);
11+
12+
vertexA
13+
.addEdge(edgeAB)
14+
.addEdge(edgeAC);
15+
16+
expect(vertexA.getNeighbors()).toStrictEqual([vertexB, vertexC]);
17+
});

src/data-structures/graph/graph.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/* eslint-disable no-unused-vars */
2+
import {GraphVertex} from './graphVertex';
3+
import {GraphEdge} from './graphEdge';
4+
5+
/**
6+
*
7+
*
8+
* @export
9+
* @class Graph
10+
*/
11+
export class Graph {
12+
directed: boolean;
13+
verticies: {};
14+
15+
/**
16+
* Creates an instance of Graph.
17+
* @param {boolean} directed
18+
* @memberof Graph
19+
*/
20+
constructor(directed: boolean) {
21+
this.directed = directed;
22+
this.verticies = {};
23+
}
24+
25+
/**
26+
* Add a vertex to the graph
27+
*
28+
* @param {GraphVertex} vertex
29+
* @return {Graph}
30+
* @memberof Graph
31+
*/
32+
addVertex(vertex: GraphVertex): Graph {
33+
this.verticies[vertex.getKey()] = vertex;
34+
return this;
35+
}
36+
37+
/**
38+
* @param {GraphEdge} edge
39+
* @return {Graph}
40+
* @memberof Graph
41+
*/
42+
addEdge(edge: GraphEdge): Graph {
43+
const start = edge.startVertex;
44+
if (!this.getVertex(start)) {
45+
this.addVertex(edge.startVertex);
46+
}
47+
48+
const end = edge.endVertex;
49+
if (!this.getVertex(end)) {
50+
this.addVertex(edge.endVertex);
51+
}
52+
53+
if (this.directed) {
54+
this.getVertex(start).addEdge(edge);
55+
} else {
56+
this.getVertex(start).addEdge(edge);
57+
this.getVertex(end).addEdge(edge);
58+
}
59+
60+
return this;
61+
}
62+
63+
/**
64+
* @param {GraphVertex} vertex
65+
* @return {GraphVertex}
66+
* @memberof Graph
67+
*/
68+
getVertex(vertex: GraphVertex): GraphVertex {
69+
return this.verticies[vertex.getKey()] || null;
70+
}
71+
72+
/**
73+
* @param {GraphVertex} vertex
74+
* @return {GraphVertex[]}
75+
* @memberof Graph
76+
*/
77+
getNeighbors(vertex: GraphVertex): GraphVertex[] {
78+
return this.getVertex(vertex).getNeighbors();
79+
}
80+
81+
/**
82+
* @return {string}
83+
* @memberof Graph
84+
*/
85+
toString(): string {
86+
return Object.keys(this.verticies).toString();
87+
}
88+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// eslint-disable-next-line no-unused-vars
2+
import {GraphVertex} from './graphVertex';
3+
4+
/**
5+
*
6+
*
7+
* @export
8+
* @class GraphConfig
9+
*/
10+
export class GraphConfig {
11+
enterVertex: (vertex: GraphVertex) => void;
12+
leaveVertex: (vertex: GraphVertex) => void;
13+
allowEnterVertex?: (vertex: GraphVertex) => boolean;
14+
15+
/**
16+
*Creates an instance of GraphConfig.
17+
* @param {*} {enterVertex, leaveVertex, allowEnterVertex}
18+
* @memberof GraphConfig
19+
*/
20+
constructor({enterVertex, leaveVertex, allowEnterVertex}) {
21+
this.enterVertex = enterVertex;
22+
this.leaveVertex = leaveVertex;
23+
this.allowEnterVertex = allowEnterVertex;
24+
}
25+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// eslint-disable-next-line no-unused-vars
2+
import {GraphVertex} from './graphVertex';
3+
4+
/**
5+
*
6+
*
7+
* @export
8+
* @class GraphEdge
9+
*/
10+
export class GraphEdge {
11+
startVertex: GraphVertex;
12+
endVertex: GraphVertex;
13+
14+
/**
15+
*Creates an instance of GraphEdge.
16+
* @param {GraphVertex} startVertex
17+
* @param {GraphVertex} endVertex
18+
* @memberof GraphEdge
19+
*/
20+
constructor(startVertex: GraphVertex, endVertex: GraphVertex) {
21+
this.startVertex = startVertex;
22+
this.endVertex = endVertex;
23+
}
24+
}

0 commit comments

Comments
 (0)