Skip to content
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
2 changes: 1 addition & 1 deletion DEVELOPMENT_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ This document outlines possible improvements and future developments for the `TS

## New Data Structures

- **Graph**: Implement a basic graph structure with common algorithms such as BFS, DFS, and shortest path algorithms.
- **Graph**: Implement a basic graph structure with common algorithms such as BFS, DFS, and shortest path algorithms. *(Implemented)*
- **PriorityQueue**: Provide a priority queue implementation for efficient task scheduling. *(Implemented)*
- **Balanced Trees**: Add self-balancing tree variants (e.g., AVL or Red-Black trees) for more efficient search and insert operations.

Expand Down
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,30 @@ pq.size(); // 2

```

## Graph<T>

#### addVertex(value: T)
Adds a vertex to the graph.

#### addEdge(from: T, to: T, weight?: number)
Adds a directed edge between two vertices with optional weight.

#### bfs(start: T): T[]
Traverse the graph in breadth-first order starting from the given vertex.

#### dfs(start: T): T[]
Traverse the graph in depth-first order starting from the given vertex.

#### shortestPath(start: T, end: T): T[]
Returns the shortest path between two vertices.

```typescript
const g = new Graph<number>();
g.addEdge(1, 2);
g.addEdge(1, 3);
const path = g.shortestPath(1, 3);
```

## BinaryTree<T>

#### getRoot(): BinaryTreeNode<T>
Expand Down Expand Up @@ -456,7 +480,6 @@ Checks if the tree is empty (i.e., has no nodes).
Traverse the tree in breadth-first order and apply the given callback function to each node.

# Todo
#### 1) Graph


If you have an advice, please feel free to contact with me
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "datastructure-ts",
"version": "0.1.21",
"version": "0.1.22",
"description": "Collection of data structures(LinkedList, DoubleLinkedList, Stack, Queue, Dictionary and etc...) for TypeScript.",
"scripts": {
"build": "tsc",
Expand Down
150 changes: 150 additions & 0 deletions src/Graph/Graph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
export class Graph<T> {

// =========================================================================
// Private properties
// =========================================================================
private adjacency: Map<T, Map<T, number>> = new Map();

// =========================================================================
// Public methods
// =========================================================================

/**
* Adds a vertex to the graph.
* @param value Vertex value
*/
public addVertex(value: T): void {
if (!this.adjacency.has(value)) {
this.adjacency.set(value, new Map());
}
}

/**
* Adds an edge between two vertices.
* @param from Source vertex
* @param to Destination vertex
* @param weight Weight of the edge (default 1)
*/
public addEdge(from: T, to: T, weight: number = 1): void {
this.addVertex(from);
this.addVertex(to);
this.adjacency.get(from).set(to, weight);
}

/**
* Performs breadth-first search starting from a vertex.
* @param start Starting vertex
* @returns Array of visited vertices in BFS order
*/
public bfs(start: T): T[] {
if (!this.adjacency.has(start)) {
return [];
}
const visited = new Set<T>();
const queue: T[] = [];
const result: T[] = [];
visited.add(start);
queue.push(start);
while (queue.length > 0) {
const vertex = queue.shift();
result.push(vertex);
for (const neighbour of this.adjacency.get(vertex).keys()) {
if (!visited.has(neighbour)) {
visited.add(neighbour);
queue.push(neighbour);
}
}
}
return result;
}

/**
* Performs depth-first search starting from a vertex.
* @param start Starting vertex
* @returns Array of visited vertices in DFS order
*/
public dfs(start: T): T[] {
const result: T[] = [];
const visited = new Set<T>();
const dfsVisit = (vertex: T): void => {
visited.add(vertex);
result.push(vertex);
for (const neighbour of this.adjacency.get(vertex).keys()) {
if (!visited.has(neighbour)) {
dfsVisit(neighbour);
}
}
};
if (this.adjacency.has(start)) {
dfsVisit(start);
}
return result;
}

/**
* Finds the shortest path between two vertices using Dijkstra's algorithm.
* Edge weights must be non-negative.
* @param start Source vertex
* @param end Destination vertex
* @returns Array representing the path from start to end (inclusive). Empty if no path.
*/
public shortestPath(start: T, end: T): T[] {
if (!this.adjacency.has(start) || !this.adjacency.has(end)) {
return [];
}
const distances = new Map<T, number>();
const previous = new Map<T, T | null>();
const queue = new Set<T>();

for (const vertex of this.adjacency.keys()) {
distances.set(vertex, Infinity);
previous.set(vertex, null);
queue.add(vertex);
}
distances.set(start, 0);

while (queue.size > 0) {
let minVertex: T = null;
let minDistance = Infinity;
for (const vertex of queue) {
const dist = distances.get(vertex);
if (dist < minDistance) {
minDistance = dist;
minVertex = vertex;
}
}

if (minVertex === null) {
break;
}
queue.delete(minVertex);

if (minVertex === end) {
break;
}

const neighbours = this.adjacency.get(minVertex);
for (const [n, weight] of neighbours) {
const alt = distances.get(minVertex) + weight;
if (alt < distances.get(n)) {
distances.set(n, alt);
previous.set(n, minVertex);
}
}
}

const path: T[] = [];
let current: T = end;
if (!previous.has(end) && start !== end) {
return [];
}
while (current !== null && previous.has(current)) {
path.unshift(current);
current = previous.get(current);
}
if (current === start) {
path.unshift(start);
}
return path;
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export * from './Queue/Queue';
export * from './PriorityQueue/PriorityQueue';
export * from './Nodes/BinaryTreeNode';
export * from './BinaryTree/BinaryTree';
export * from './Graph/Graph';
39 changes: 39 additions & 0 deletions tests/Graph/Graph.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Graph } from '../../src/Graph/Graph';
import { expect } from 'chai';

describe('Graph', () => {
let graph: Graph<string>;

beforeEach(() => {
graph = new Graph<string>();
});

it('bfs should visit nodes in breadth first order', () => {
graph.addEdge('A', 'B');
graph.addEdge('A', 'C');
graph.addEdge('B', 'D');
graph.addEdge('C', 'E');

const order = graph.bfs('A');
expect(order).to.deep.equal(['A', 'B', 'C', 'D', 'E']);
});

it('dfs should visit nodes in depth first order', () => {
graph.addEdge('A', 'B');
graph.addEdge('A', 'C');
graph.addEdge('B', 'D');
graph.addEdge('C', 'E');

const order = graph.dfs('A');
expect(order).to.deep.equal(['A', 'B', 'D', 'C', 'E']);
});

it('shortestPath should return path with minimal weight', () => {
graph.addEdge('A', 'B', 1);
graph.addEdge('B', 'C', 2);
graph.addEdge('A', 'C', 5);

const path = graph.shortestPath('A', 'C');
expect(path).to.deep.equal(['A', 'B', 'C']);
});
});
3 changes: 2 additions & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"no-increment-decrement": [false],
"no-parameter-reassignment": [false],
"ter-indent": [false],
"no-else-after-return": [false]
"no-else-after-return": [false],
"object-curly-spacing": false
}
}