Table of Contents
842 words
4 minutes
Graphs: TypeScript Implementations of Adjacency Lists, DFS, BFS, and Topological Sort

Overview#

A graph uses vertices and edges to describe relationships between objects. Compared with arrays, linked lists, and trees, graphs are better suited to expressing “many-to-many connections,” such as social relationships, dependency relationships, map roads, and task scheduling.

Prerequisites

  • Hash table / array: commonly used to store adjacency lists
  • DFS / BFS: the foundational algorithms for graph traversal
  • In-degree: used in topological sorting to determine whether a node can be processed

Problem Definition#

Given a set of vertices and a set of edges, choose an appropriate representation and complete traversal, connectivity determination, or dependency ordering.

ElementDescription
InputNumber of vertices, edge list, direction, and weights
OutputAdjacency structure, traversal result, topological order, or number of connected components
Common representationsAdjacency list, adjacency matrix, edge list
Typical problemsDFS/BFS, course schedule, connected components, shortest path

Core Principle: Step-by-Step Diagram#

The directed graph edge list [[0,1], [0,2], [1,3], [2,3]] can be represented as an adjacency list:

0 1 2 3

The corresponding structure:

0 -> [1, 2]
1 -> [3]
2 -> [3]
3 -> []

Intuition Behind Topological Sorting#

If an edge u -> v means “u must be completed before v can be completed,” then repeatedly taking out nodes with in-degree 0 yields a valid dependency execution order.


Detailed Algorithm Steps#

Algorithm: TopologicalSort(n, edges)
Input: number of nodes n, dependency edges edges
Output: topological order; returns an empty array if there is a cycle
1. Build adjacency list graph and indegree array indegree
2. Enqueue all nodes with indegree 0
3. while the queue is not empty:
4. dequeue node and add it to the result
5. iterate over node's neighbor next:
6. indegree[next] -= 1
7. if indegree[next] == 0, enqueue
8. If the result length is n, return the result; otherwise it means there is a cycle

Complexity analysis:

OperationTime complexitySpace complexityDescription
Build adjacency listO(V + E)O(V + E)Initialize nodes and add edges
DFS / BFSO(V + E)O(V)Each vertex and edge is visited at most once
Topological sortO(V + E)O(V + E)Adjacency list and in-degree array
Query edge in adjacency matrixO(1)O(V²)Suitable for dense graphs

TypeScript Implementation#

1. Building an Adjacency List#

type AdjList = Map<number, number[]>;
function buildGraph(n: number, edges: number[][], directed = true): AdjList {
const graph: AdjList = new Map();
for (let i = 0; i < n; i++) {
graph.set(i, []);
}
for (const [from, to] of edges) {
graph.get(from)!.push(to);
if (!directed) graph.get(to)!.push(from);
}
return graph;
}

2. DFS Traversal#

function dfs(graph: AdjList, start: number): number[] {
const visited = new Set<number>();
const result: number[] = [];
function visit(node: number): void {
if (visited.has(node)) return;
visited.add(node);
result.push(node);
for (const next of graph.get(node) ?? []) {
visit(next);
}
}
visit(start);
return result;
}

3. BFS Traversal#

function bfs(graph: AdjList, start: number): number[] {
const queue = [start];
const visited = new Set<number>([start]);
const result: number[] = [];
let head = 0;
while (head < queue.length) {
const node = queue[head++];
result.push(node);
for (const next of graph.get(node) ?? []) {
if (visited.has(next)) continue;
visited.add(next);
queue.push(next);
}
}
return result;
}

4. Topological Sort#

function topologicalSort(numCourses: number, prerequisites: number[][]): number[] {
const graph = buildGraph(numCourses, prerequisites);
const indegree = new Array(numCourses).fill(0);
for (const [, to] of prerequisites) {
indegree[to]++;
}
const queue: number[] = [];
for (let i = 0; i < numCourses; i++) {
if (indegree[i] === 0) queue.push(i);
}
const result: number[] = [];
let head = 0;
while (head < queue.length) {
const node = queue[head++];
result.push(node);
for (const next of graph.get(node) ?? []) {
indegree[next]--;
if (indegree[next] === 0) queue.push(next);
}
}
return result.length === numCourses ? result : [];
}

5. Number of Connected Components#

function findCircleNum(isConnected: number[][]): number {
const n = isConnected.length;
const visited = new Set<number>();
let provinces = 0;
function dfsCity(city: number): void {
visited.add(city);
for (let next = 0; next < n; next++) {
if (isConnected[city][next] === 1 && !visited.has(next)) {
dfsCity(next);
}
}
}
for (let city = 0; city < n; city++) {
if (!visited.has(city)) {
provinces++;
dfsCity(city);
}
}
return provinces;
}

Engineering Optimization: Adjacency List or Adjacency Matrix#

RepresentationAdvantagesCostApplicable scenarios
Adjacency listSpace O(V + E), fast neighbor traversalDetermining whether an edge exists between two points requires traversing neighborsSparse graphs, traversal-type problems
Adjacency matrixEdge lookup O(1)Space O(V²)Dense graphs, small number of nodes
Edge listConvenient input and sortingSlow neighbor lookupKruskal, Bellman-Ford

In JavaScript/TypeScript, if node indices are contiguous, number[][] is usually lighter than Map<number, number[]>; if nodes are strings or sparse IDs, Map is more natural.


Applications and Limitations#

Typical Applications#

  • Dependency analysis: course schedules, task orchestration, package management
  • Social networks, recommendation systems, relationship chain search
  • Map paths, network routing, shortest paths
  • Connected components, cycle detection, topological sorting

Limitations#

LimitationDescription
High modeling costThe meaning of nodes and edges must first be clarified
Memory pressure for large graphsThe adjacency structure may be very large
Algorithm choice depends on graph typeDirected/undirected, weighted/unweighted, and whether cycles exist all affect the solution

Summary#

Edge list input Build graph representation DFS/BFS traversal In-degree counting Topological sort Connectivity/path problems

Key points:

  1. Graphs are used to express many-to-many relationships, and adjacency lists are the most commonly used representation.
  2. DFS is suitable for connectivity and deep exploration, while BFS is suitable for level order and unweighted shortest paths.
  3. Topological sorting relies on repeatedly dequeuing nodes with in-degree 0.
  4. First determine the graph type, then choose the appropriate data structure and algorithm.
Graphs: TypeScript Implementations of Adjacency Lists, DFS, BFS, and Topological Sort
https://www.hehonglei.com/technology/graph/
Author
HHLBlog
Published at
2026-04-26
License
CC BY-NC-SA 4.0