38
loading...
This website collects cookies to deliver better user experience
Adjacency list - Every node stores a list of adjacent vertices, for example, an array or that contains all vertices and each vertex contains another array with adjacent vertices, other data structures can be used instead of an array, like a hash table and a linked list.
Adjacency matrix - An NxN boolean matrix (where N is the number of vertices), if the matrix[i][j] stores the value true, there is a connection between the vertices i and j. In an undirected graph matrix[j][i] also will store the value true. You can use other types instead of boolean, for example, numbers to represent weight.
export class Node<T> {
data: T;
adjacent: Node<T>[];
comparator: (a: T, b: T) => number;
constructor(data: T, comparator: (a: T, b: T) => number) {
this.data = data;
this.adjacent = [];
this.comparator = comparator;
}
addAdjacent(node: Node<T>): void {
this.adjacent.push(node);
}
removeAdjacent(data: T): Node<T> | null {
const index = this.adjacent.findIndex(
(node) => this.comparator(node.data, data) === 0
);
if (index > -1) {
return this.adjacent.splice(index, 1)[0];
}
return null;
}
}
class Graph<T> {
nodes: Map<T, Node<T>> = new Map();
comparator: (a: T, b: T) => number;
constructor(comparator: (a: T, b: T) => number) {
this.comparator = comparator;
}
/**
* Add a new node if it was not added before
*
* @param {T} data
* @returns {Node<T>}
*/
addNode(data: T): Node<T> {
let node = this.nodes.get(data);
if (node) return node;
node = new Node(data, this.comparator);
this.nodes.set(data, node);
return node;
}
/**
* Remove a node, also remove it from other nodes adjacency list
*
* @param {T} data
* @returns {Node<T> | null}
*/
removeNode(data: T): Node<T> | null {
const nodeToRemove = this.nodes.get(data);
if (!nodeToRemove) return null;
this.nodes.forEach((node) => {
node.removeAdjacent(nodeToRemove.data);
});
this.nodes.delete(data);
return nodeToRemove;
}
/**
* Create an edge between two nodes
*
* @param {T} source
* @param {T} destination
*/
addEdge(source: T, destination: T): void {
const sourceNode = this.addNode(source);
const destinationNode = this.addNode(destination);
sourceNode.addAdjacent(destinationNode);
}
/**
* Remove an edge between two nodes
*
* @param {T} source
* @param {T} destination
*/
removeEdge(source: T, destination: T): void {
const sourceNode = this.nodes.get(source);
const destinationNode = this.nodes.get(destination);
if (sourceNode && destinationNode) {
sourceNode.removeAdjacent(destination);
}
}
/**
* Depth-first search
*
* @param {T} data
* @param {Map<T, boolean>} visited
* @returns
*/
private depthFirstSearchAux(node: Node<T>, visited: Map<T, boolean>): void {
if (!node) return;
visited.set(node.data, true);
console.log(node.data);
node.adjacent.forEach((item) => {
if (!visited.has(item.data)) {
this.depthFirstSearchAux(item, visited);
}
});
}
depthFirstSearch() {
const visited: Map<T, boolean> = new Map();
this.nodes.forEach((node) => {
if (!visited.has(node.data)) {
this.depthFirstSearchAux(node, visited);
}
});
}
/**
* Breadth-first search
*
* @param {T} data
* @returns
*/
private breadthFirstSearchAux(node: Node<T>, visited: Map<T, boolean>): void {
const queue: Queue<Node<T>> = new Queue();
if (!node) return;
queue.add(node);
visited.set(node.data, true);
while (!queue.isEmpty()) {
node = queue.remove();
if (!node) continue;
console.log(node.data);
node.adjacent.forEach((item) => {
if (!visited.has(item.data)) {
visited.set(item.data, true);
queue.add(item);
}
});
}
}
breadthFirstSearch() {
const visited: Map<T, boolean> = new Map();
this.nodes.forEach((node) => {
if (!visited.has(node.data)) {
this.breadthFirstSearchAux(node, visited);
}
});
}
}
function comparator(a: number, b: number) {
if (a < b) return -1;
if (a > b) return 1;
return 0;
}
const graph = new Graph(comparator);