Skip to main content

Graphs

Graph is a data structure that is a collection of nodes and its connections.

It consists of vertex - a node, edge - connections between them.

Graphs are used in social networks, locations, routing algorithms, file system optimization etc.

There are types of graphs.

  • Undirected graph : An undirected graph is a graph where edges do not have a specific direction, meaning connections go both ways.
  • Directed graph: A graph in which edges have a direction, i.e., the edges have arrows indicating the direction of traversal.
  • Weighted graph: Has units on the edges. Often used with algorithms to know shortest path.

Adjacency matrix​

An adjacency matrix is a 2D matrix A of size n × n, where n is the number of vertices in the graph. The elements of the matrix indicate whether pairs of vertices are adjacent or not in the graph.

Adjacency List​

An adjacency list is a common way to represent graphs. In this structure:

  • Each vertex stores a list of adjacent vertices.
  • It's typically implemented using an array (or dictionary) of lists.
  • It is very efficient for sparse graphs, where the number of edges is much less than the square of the number of vertices.

Time Complexity between Adjacency List and Adjacency Matrix​

FeatureAdjacency ListAdjacency Matrix
Add VertexO(1)O(V2)
Add an EdgeO(1)O(1)
Remove VertexO(V + E)O(V2)
Remove an EdgeO(E)O(1)
Edge Lookup TimeO(V + E)O(1)
StorageO(V + E)O(V2)
note

V is the number of vertices and E is the Edges of the graph

Graph Traversal​

Graph traversal is the process of visiting all the nodes (or vertices) in a graph, typically to search or explore the structure. The two main types of graph traversal are Depth-First Search (DFS) and Breadth-First Search (BFS).

In DFS, you start from a node and explore as far as possible along each branch before backtracking. In BFS, you explore all the neighbors of a node before moving on to their neighbors.

Pseudocode Depth First Traversal​

There is a recursive and an iterative solution. both will give a different order.

  • The function should take a starting node.
  • Create a list to store the end result.
  • Create an object to store visited vertices.
  • Create a helper function which accepts a vertex
    • This helper function should return early if the vertex is empty.
    • The helper function should place the vertex it accepts into the visited object and push that vertex into the result array.
    • Loop over all of the values in the adjacency list for that vertex.
    • If any of those values are not listed/visited, recursively invoke the helper function with that vertex.

Pseudocode Breadth First Traversal​

  • The function should take a starting node.
  • Create a queue and place the starting vertex in it.
  • Create an array to store the nodes visited.
  • Create an object to store visited vertices.
  • Mark the starting vertex as visited.
  • Loop as long as there is anything in the queue.
  • Remove the first vertex from the queue and push it into the array that stores the nodes visited.
  • Loop over each vertex in the adjacency list for the vertex you are visiting.
  • If it is not inside the object that stores nodes visited, mark it as visited and enqueue that vertex.
  • Return the array of visited nodes.

Implementation​

Below implementation with undirected graphs.

Graph implementation
// * Undirected
// * not doing error handling
class Graph {
constructor() {
this.adjacencyList = {};
}
addVertex(vertex) {
if (!this.adjacencyList[vertex]) this.adjacencyList[vertex] = [];
}
addEdge(vertex1, vertex2) {
this.adjacencyList[vertex1].push(vertex2);
this.adjacencyList[vertex2].push(vertex1);
}
removeEdge(vertex1, vertex2) {
this.adjacencyList[vertex1] = this.adjacencyList[vertex1].filter(
(v) => v !== vertex2
);
this.adjacencyList[vertex2] = this.adjacencyList[vertex2].filter(
(v) => v !== vertex1
);
}
removeVertex(vertex) {
while (this.adjacencyList[vertex].length) {
const adjacentVertex = this.adjacencyList[vertex].pop();
this.removeEdge(vertex, adjacentVertex);
}
delete this.adjacencyList[vertex];
}
// recursive
depthFirstTraversal(start) {
const result = [];
const visited = {};
const adjacencyList = this.adjacencyList;
//IIFE. Can do without IIFE as well
(function dfs(vertex) {
if (!vertex) return null;
visited[vertex] = true;
result.push(vertex);
adjacencyList[vertex].forEach((neighbor) => {
if (!visited[neighbor]) {
return dfs(neighbor);
}
});
})(start);
return result;
}
// Iterative.
dfsIterative(start) {
let stack = [start];
let visited = {};
let currentVertex;
let result = [];
visited[start] = true;
while (stack.length) {
currentVertex = stack.pop();
result.push(currentVertex);
this.adjacencyList[currentVertex].forEach((neighbor) => {
if (!visited[neighbor]) {
visited[neighbor] = true;
stack.push(neighbor);
}
});
}
return result;
}
breadthFirst(start) {
const queue = [start];
const result = [];
const visited = {};
let currentVertex;
visited[start] = true;

while (queue.length) {
currentVertex = queue.shift();
result.push(currentVertex);
this.adjacencyList(currentVertex).forEach((neighbor) => {
if (!visited[neighbor]) {
visited[neighbor] = true;
queue.push(neighbor);
}
});
}
return result;
}
}