Skip to content
Code Kata
Go back

Kata: Graph Representation and Traversal

Suggest edit

Table of contents

Open Table of contents

Problem Statement

Implement an undirected graph that supports:

Concepts

A graph is a collection of vertices (nodes) connected by edges. The adjacency list representation stores each vertex’s neighbors in a list, providing space-efficient storage for sparse graphs.

TypeScript: Uses Map<string, string[]> for the adjacency list. See TypeScript Core Concepts.

Python: defaultdict(list) is ideal for adjacency lists. See Python Core Concepts.

Boilerplate

TypeScript

// graph.ts
class Graph {
  private adjacencyList: Map<string, string[]> = new Map();

  addVertex(vertex: string): void { /* TODO */ }
  addEdge(v1: string, v2: string): void { /* TODO */ }
  bfs(start: string): string[] { /* TODO */ return []; }
  dfs(start: string): string[] { /* TODO */ return []; }
  hasPath(start: string, end: string): boolean { /* TODO */ return false; }
}

Python

# graph.py
from collections import defaultdict, deque

class Graph:
    def __init__(self) -> None:
        self._adj: defaultdict[str, list[str]] = defaultdict(list)

    def add_vertex(self, vertex: str) -> None: ...
    def add_edge(self, v1: str, v2: str) -> None: ...
    def bfs(self, start: str) -> list[str]: ...
    def dfs(self, start: str) -> list[str]: ...
    def has_path(self, start: str, end: str) -> bool: ...

Hints

Hint 1: BFS

Use a queue (FIFO). Enqueue the start, mark it visited. While the queue isn’t empty, dequeue a vertex, process it, and enqueue its unvisited neighbors.

Hint 2: DFS (iterative)

Same as BFS but use a stack (LIFO) instead of a queue. Or use recursion.

Hint 3: hasPath

BFS or DFS from start. If you visit end, return true. If the traversal completes without visiting end, return false.

Solution

TypeScript

class Graph {
  private adjacencyList: Map<string, string[]> = new Map();

  addVertex(vertex: string): void {
    if (!this.adjacencyList.has(vertex)) {
      this.adjacencyList.set(vertex, []);
    }
  }

  addEdge(v1: string, v2: string): void {
    this.addVertex(v1);
    this.addVertex(v2);
    this.adjacencyList.get(v1)!.push(v2);
    this.adjacencyList.get(v2)!.push(v1);
  }

  bfs(start: string): string[] {
    const visited = new Set<string>();
    const result: string[] = [];
    const queue: string[] = [start];
    visited.add(start);

    while (queue.length > 0) {
      const vertex = queue.shift()!;
      result.push(vertex);

      for (const neighbor of this.adjacencyList.get(vertex) ?? []) {
        if (!visited.has(neighbor)) {
          visited.add(neighbor);
          queue.push(neighbor);
        }
      }
    }

    return result;
  }

  dfs(start: string): string[] {
    const visited = new Set<string>();
    const result: string[] = [];
    const stack: string[] = [start];

    while (stack.length > 0) {
      const vertex = stack.pop()!;
      if (visited.has(vertex)) continue;

      visited.add(vertex);
      result.push(vertex);

      for (const neighbor of this.adjacencyList.get(vertex) ?? []) {
        if (!visited.has(neighbor)) {
          stack.push(neighbor);
        }
      }
    }

    return result;
  }

  hasPath(start: string, end: string): boolean {
    const visited = new Set<string>();
    const queue: string[] = [start];
    visited.add(start);

    while (queue.length > 0) {
      const vertex = queue.shift()!;
      if (vertex === end) return true;

      for (const neighbor of this.adjacencyList.get(vertex) ?? []) {
        if (!visited.has(neighbor)) {
          visited.add(neighbor);
          queue.push(neighbor);
        }
      }
    }

    return false;
  }
}

Python

from collections import defaultdict, deque

class Graph:
    def __init__(self) -> None:
        self._adj: defaultdict[str, list[str]] = defaultdict(list)

    def add_vertex(self, vertex: str) -> None:
        if vertex not in self._adj:
            self._adj[vertex] = []

    def add_edge(self, v1: str, v2: str) -> None:
        self._adj[v1].append(v2)
        self._adj[v2].append(v1)

    def bfs(self, start: str) -> list[str]:
        visited = {start}
        result: list[str] = []
        queue = deque([start])

        while queue:
            vertex = queue.popleft()
            result.append(vertex)

            for neighbor in self._adj[vertex]:
                if neighbor not in visited:
                    visited.add(neighbor)
                    queue.append(neighbor)

        return result

    def dfs(self, start: str) -> list[str]:
        visited: set[str] = set()
        result: list[str] = []
        stack = [start]

        while stack:
            vertex = stack.pop()
            if vertex in visited:
                continue

            visited.add(vertex)
            result.append(vertex)

            for neighbor in self._adj[vertex]:
                if neighbor not in visited:
                    stack.append(neighbor)

        return result

    def has_path(self, start: str, end: str) -> bool:
        visited = {start}
        queue = deque([start])

        while queue:
            vertex = queue.popleft()
            if vertex == end:
                return True
            for neighbor in self._adj[vertex]:
                if neighbor not in visited:
                    visited.add(neighbor)
                    queue.append(neighbor)

        return False

Complexity Analysis

OperationTimeSpace
Add VertexO(1)O(1)
Add EdgeO(1)O(1)
BFSO(V + E)O(V)
DFSO(V + E)O(V)
Has PathO(V + E)O(V)

Where V = vertices, E = edges.

Cross-Language Notes

Python: The combination of defaultdict(list) and deque makes Python particularly elegant for graph algorithms. The set type provides O(1) membership testing for the visited set.

PHP: Use associative arrays (array<string, array<string>>) for adjacency lists. PHP’s SplQueue works well for BFS. See PHP Core Concepts.

Rust: The petgraph crate provides a comprehensive graph library with multiple representations and algorithms.

See also: Kata: Depth-First Search and Kata: Dijkstra’s Shortest Path for more advanced graph algorithms.


Suggest edit
Share this post on:

Previous Post
Kata: Observer Pattern
Next Post
Kata: Stack and Queue