Graph Interview Patterns: A Complete Guide (2025)

Graph problems appear in every serious technical interview. They cover a wide range of difficulty — from simple BFS/DFS traversal to Dijkstra and Union-Find. The good news: most graph interview problems reduce to one of six patterns. Recognizing the pattern is half the solution.

Graph Representation

Two standard representations:

# Adjacency list (most common for sparse graphs)
from collections import defaultdict
graph = defaultdict(list)
for u, v in edges:
    graph[u].append(v)
    graph[v].append(u)  # undirected

# Adjacency matrix (dense graphs or when O(1) edge lookup needed)
n = 5
adj = [[0]*n for _ in range(n)]
for u, v in edges:
    adj[u][v] = adj[v][u] = 1

Pattern 1: Connected Components (DFS / BFS)

Count connected components or check if all nodes are reachable. Classic: Number of Islands, Number of Provinces.

def numComponents(n, edges):
    graph = defaultdict(list)
    for u, v in edges:
        graph[u].append(v); graph[v].append(u)

    visited = set()
    count = 0

    def dfs(node):
        visited.add(node)
        for neighbor in graph[node]:
            if neighbor not in visited:
                dfs(neighbor)

    for i in range(n):
        if i not in visited:
            dfs(i)
            count += 1
    return count

For grids: treat each cell as a node, neighbors are the 4 (or 8) adjacent cells. Mark visited by modifying the grid in-place to avoid an extra set.

Pattern 2: Shortest Path in Unweighted Graph (BFS)

BFS guarantees the shortest path in unweighted graphs — the first time a node is reached is via the shortest path. Use for: minimum number of steps, minimum word transformations (Word Ladder), minimum moves on a grid.

from collections import deque

def bfs_shortest_path(graph, start, end):
    queue = deque([(start, 0)])  # (node, distance)
    visited = {start}
    while queue:
        node, dist = queue.popleft()
        if node == end:
            return dist
        for neighbor in graph[node]:
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append((neighbor, dist + 1))
    return -1  # no path

Multi-source BFS: add all sources to the queue initially. The distance to each cell is from the nearest source. Used for: 01 Matrix (distance to nearest 0), Rotting Oranges (distance from any rotten orange).

Pattern 3: Shortest Path in Weighted Graph (Dijkstra)

Dijkstra finds shortest paths from a source when edge weights are non-negative. Time O((V + E) log V) with a min-heap.

import heapq

def dijkstra(graph, start):
    # graph[u] = [(weight, v), ...]
    dist = {node: float("inf") for node in graph}
    dist[start] = 0
    heap = [(0, start)]  # (distance, node)

    while heap:
        d, u = heapq.heappop(heap)
        if d > dist[u]:
            continue  # stale entry, skip
        for weight, v in graph[u]:
            if dist[u] + weight < dist[v]:
                dist[v] = dist[u] + weight
                heapq.heappush(heap, (dist[v], v))
    return dist

LeetCode applications: Network Delay Time (743), Cheapest Flights Within K Stops (787, modified Dijkstra), Path With Minimum Effort (1631).

Pattern 4: Topological Sort (Kahn or DFS)

Order nodes such that all edges point forward. Use for: task scheduling, course prerequisites, build dependency resolution.

from collections import deque

def topological_sort(n, prerequisites):
    in_degree = [0] * n
    graph = defaultdict(list)
    for a, b in prerequisites:
        graph[b].append(a)
        in_degree[a] += 1

    queue = deque(i for i in range(n) if in_degree[i] == 0)
    order = []

    while queue:
        node = queue.popleft()
        order.append(node)
        for neighbor in graph[node]:
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.append(neighbor)

    return order if len(order) == n else []  # empty list = cycle

Pattern 5: Union-Find (Disjoint Set Union)

Dynamically group elements and query if two elements are in the same group. O(alpha(n)) ≈ O(1) amortized with path compression and union by rank.

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * n
        self.components = n

    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])  # path compression
        return self.parent[x]

    def union(self, x, y):
        px, py = self.find(x), self.find(y)
        if px == py:
            return False  # already connected
        if self.rank[px] < self.rank[py]:
            px, py = py, px
        self.parent[py] = px
        if self.rank[px] == self.rank[py]:
            self.rank[px] += 1
        self.components -= 1
        return True

Use for: detecting cycles in undirected graphs, minimum spanning tree (Kruskal), redundant connection, accounts merge.

Pattern 6: Cycle Detection

Undirected: Union-Find or DFS (track parent to avoid revisiting). Directed: DFS with three-color marking (white=unvisited, gray=in-stack, black=done). If you encounter a gray node, there is a cycle.

def hasCycle(n, edges):
    # Directed graph
    graph = defaultdict(list)
    for u, v in edges:
        graph[u].append(v)

    WHITE, GRAY, BLACK = 0, 1, 2
    color = [WHITE] * n

    def dfs(u):
        color[u] = GRAY
        for v in graph[u]:
            if color[v] == GRAY:
                return True  # back edge = cycle
            if color[v] == WHITE and dfs(v):
                return True
        color[u] = BLACK
        return False

    return any(dfs(i) for i in range(n) if color[i] == WHITE)

Grid as Graph

Many graph problems disguise themselves as grid problems. Treat each cell (r, c) as node r*cols+c. Neighbors are the 4 adjacent cells. Apply BFS/DFS/Union-Find as normal. Common patterns: flood fill (Number of Islands), BFS for minimum distance (01 Matrix, Walls and Gates), DFS for reachability (Pacific Atlantic Water Flow, Surrounded Regions).

Recognition Guide

Pattern Signal in Problem
BFS (unweighted shortest) “minimum steps,” “minimum transformations,” level-order
Dijkstra “minimum cost/distance,” weighted graph, non-negative weights
Bellman-Ford “negative weights,” “detect negative cycle,” “at most K edges”
Topological sort “dependency order,” “course prerequisites,” DAG
Union-Find “connected components,” “number of groups,” “merge accounts”
DFS backtrack “find all paths,” “paths with constraint”

Frequently Asked Questions

When should you use Dijkstra vs BFS vs Bellman-Ford?

BFS finds shortest paths in unweighted graphs (or where all edge weights are equal) in O(V + E) time. It is the first choice for: minimum hops, minimum steps on a grid, Word Ladder. Dijkstra handles weighted graphs with non-negative weights in O((V + E) log V) with a min-heap. Use for: network routing, minimum cost path when weights represent distances or costs. Bellman-Ford handles negative weights and detects negative cycles in O(V * E) time — much slower than Dijkstra. Use when: the graph may have negative edges (currency arbitrage, relaxed shortest path with K stops). In LeetCode, Bellman-Ford appears in problems with a constraint on number of edges (Cheapest Flights Within K Stops) where Dijkstra without modification would not respect the K-hop limit.

How does Union-Find detect cycles in an undirected graph?

Union-Find detects cycles by tracking connected components. Initialize each node as its own component. Process edges one by one: for each edge (u, v), find the root of u and the root of v. If they share the same root, u and v are already in the same component, meaning this edge creates a cycle — return True. If different roots, union them (merge the two components) and continue. After processing all edges without finding a shared root, return False — no cycle. This approach runs in O(E * alpha(V)) ≈ O(E) time. Compare with DFS cycle detection: DFS also runs O(V + E) but requires explicit recursion and a visited/in-stack state. Union-Find is simpler for undirected graphs. For directed graphs, Union-Find does not apply because directionality matters — use three-color DFS (white/gray/black) instead.

What is topological sort and which problems require it?

Topological sort orders the nodes of a DAG (Directed Acyclic Graph) such that for every directed edge from u to v, u appears before v in the ordering. It is only possible in acyclic graphs — if a cycle exists, no valid ordering exists and Kahn's algorithm returns fewer nodes than expected. Two implementations: (1) Kahn's BFS algorithm: initialize a queue with all nodes of in-degree 0; repeatedly dequeue a node, add to result, and decrement neighbors' in-degrees; add any neighbor that reaches 0 to the queue. (2) DFS: after finishing all neighbors of a node, push it to a stack; the stack is the topological order in reverse. Problems requiring topological sort: Course Schedule I and II (LeetCode 207, 210), Alien Dictionary (reconstruct character order from sorted words), Task Scheduling (tasks with prerequisites), Build Systems (Maven, Make — build targets before their dependents). Recognition signal: "do X before Y" constraints on a set of tasks or nodes.

{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “When should you use Dijkstra vs BFS vs Bellman-Ford?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “BFS finds shortest paths in unweighted graphs (or where all edge weights are equal) in O(V + E) time. It is the first choice for: minimum hops, minimum steps on a grid, Word Ladder. Dijkstra handles weighted graphs with non-negative weights in O((V + E) log V) with a min-heap. Use for: network routing, minimum cost path when weights represent distances or costs. Bellman-Ford handles negative weights and detects negative cycles in O(V * E) time — much slower than Dijkstra. Use when: the graph may have negative edges (currency arbitrage, relaxed shortest path with K stops). In LeetCode, Bellman-Ford appears in problems with a constraint on number of edges (Cheapest Flights Within K Stops) where Dijkstra without modification would not respect the K-hop limit.”
}
},
{
“@type”: “Question”,
“name”: “How does Union-Find detect cycles in an undirected graph?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Union-Find detects cycles by tracking connected components. Initialize each node as its own component. Process edges one by one: for each edge (u, v), find the root of u and the root of v. If they share the same root, u and v are already in the same component, meaning this edge creates a cycle — return True. If different roots, union them (merge the two components) and continue. After processing all edges without finding a shared root, return False — no cycle. This approach runs in O(E * alpha(V)) ≈ O(E) time. Compare with DFS cycle detection: DFS also runs O(V + E) but requires explicit recursion and a visited/in-stack state. Union-Find is simpler for undirected graphs. For directed graphs, Union-Find does not apply because directionality matters — use three-color DFS (white/gray/black) instead.”
}
},
{
“@type”: “Question”,
“name”: “What is topological sort and which problems require it?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Topological sort orders the nodes of a DAG (Directed Acyclic Graph) such that for every directed edge from u to v, u appears before v in the ordering. It is only possible in acyclic graphs — if a cycle exists, no valid ordering exists and Kahn’s algorithm returns fewer nodes than expected. Two implementations: (1) Kahn’s BFS algorithm: initialize a queue with all nodes of in-degree 0; repeatedly dequeue a node, add to result, and decrement neighbors’ in-degrees; add any neighbor that reaches 0 to the queue. (2) DFS: after finishing all neighbors of a node, push it to a stack; the stack is the topological order in reverse. Problems requiring topological sort: Course Schedule I and II (LeetCode 207, 210), Alien Dictionary (reconstruct character order from sorted words), Task Scheduling (tasks with prerequisites), Build Systems (Maven, Make — build targets before their dependents). Recognition signal: “do X before Y” constraints on a set of tasks or nodes.”
}
}
]
}

Scroll to Top