Search Pass4Sure

Graph Algorithms Interview Prep

Prepare for graph algorithm coding interview questions with BFS, DFS, Dijkstra, Union-Find, and topological sort implementations and problem patterns.

Graph Algorithms Interview Prep

What graph algorithms do I need to know for coding interviews?

For most coding interviews at major technology companies, you need BFS (shortest paths, level-order traversal), DFS (connectivity, cycle detection, topological sorting), Dijkstra's algorithm (weighted shortest paths), and Union-Find (disjoint set problems). Binary tree problems use a subset of graph techniques but are treated separately. Knowing when to apply each algorithm is as important as knowing the implementation.


Graph problems are among the most commonly tested topics in senior-level coding interviews, and they are frequently the questions that separate candidates who have done serious preparation from those who have not. Graphs appear everywhere in real software systems — social networks, routing algorithms, dependency resolution, network flow — so interviewers use them as a proxy for systems thinking as much as algorithmic knowledge. This guide covers the algorithms, problem types, and implementation patterns you need.

Graph Fundamentals Review

Before studying algorithms, ensure your fundamental graph concepts are solid.

Graph Terminology

Vertex (node): A fundamental unit of a graph. Edge: A connection between two vertices. Directed graph (digraph): Edges have direction. Edge (u, v) means u → v but not necessarily v → u. Undirected graph: Edges are bidirectional. Edge (u, v) means both u → v and v → u. Weighted graph: Edges have associated weights or costs. Connected graph: Every vertex is reachable from every other vertex (undirected context). Strongly connected: Every vertex is reachable from every other vertex in both directions (directed context). Cycle: A path that starts and ends at the same vertex. DAG: Directed Acyclic Graph — no cycles.

Graph Representation in Code

Adjacency list (most common in interviews):

# For undirected graph with n nodes and edges list
graph = defaultdict(list)
for u, v in edges:
    graph[u].append(v)
    graph[v].append(u)  # omit for directed graph

Adjacency matrix (for dense graphs or when O(1) edge lookup needed):

matrix = [[0] * n for _ in range(n)]
for u, v in edges:
    matrix[u][v] = 1
    matrix[v][u] = 1  # omit for directed

Breadth-First Search (BFS)

BFS explores a graph level by level, visiting all vertices at distance d before visiting vertices at distance d+1. This property makes it the natural algorithm for shortest path problems in unweighted graphs.

BFS Implementation Template

from collections import deque

def bfs(graph, start):
    visited = set([start])
    queue = deque([start])
    
    while queue:
        node = queue.popleft()
        # Process node here
        
        for neighbor in graph[node]:
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append(neighbor)

Time and Space Complexity

  • Time: O(V + E) where V is vertices and E is edges
  • Space: O(V) for the visited set and queue

BFS Use Cases in Interviews

Problem Type BFS Application
Shortest path (unweighted) BFS from source; distance to each node is the level
Minimum number of operations Model each state as a node; BFS finds minimum steps
Connected components BFS/DFS from each unvisited node
Level-order tree traversal BFS where each level is processed together
Bipartite graph check BFS with two-coloring
Word ladder problem BFS where each word transformation is an edge

Depth-First Search (DFS)

DFS explores as far as possible along each branch before backtracking. It is more natural than BFS for problems involving paths, cycles, and topological ordering.

DFS Implementation Template (Recursive)

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

DFS Implementation Template (Iterative)

def dfs_iterative(graph, start):
    visited = set([start])
    stack = [start]
    
    while stack:
        node = stack.pop()
        # Process node
        
        for neighbor in graph[node]:
            if neighbor not in visited:
                visited.add(neighbor)
                stack.append(neighbor)

DFS Use Cases in Interviews

Problem Type DFS Application
Cycle detection (directed) DFS with three states: unvisited, in-progress, complete
Cycle detection (undirected) DFS tracking parent to avoid false cycles
Topological sort DFS post-order; reverse gives topological order
Number of islands Grid DFS marking visited cells
Path finding DFS with backtracking
Strongly connected components Kosaraju's or Tarjan's algorithm

Cycle Detection

Cycle detection is a standalone interview question and also a subproblem in many other problems.

Cycle Detection in Undirected Graphs (DFS)

Track the parent of each node. If you encounter a neighbor that is visited and is not the parent, you have found a cycle.

Cycle Detection in Directed Graphs (DFS Coloring)

Use three states: 0 = unvisited, 1 = in current DFS path, 2 = fully processed. If you encounter a node with state 1, you have found a cycle.

def has_cycle_directed(graph, n):
    state = [0] * n
    
    def dfs(node):
        state[node] = 1  # in progress
        for neighbor in graph[node]:
            if state[neighbor] == 1:
                return True  # cycle found
            if state[neighbor] == 0:
                if dfs(neighbor):
                    return True
        state[node] = 2  # fully processed
        return False
    
    return any(dfs(node) for node in range(n) if state[node] == 0)

Topological Sort

Topological sorting of a directed acyclic graph produces a linear ordering of vertices such that for every edge (u, v), u appears before v. This is used for dependency resolution, build order problems, and course scheduling.

Kahn's Algorithm (BFS-based)

Compute in-degree of all nodes. Add all zero-in-degree nodes to a queue. Process queue: take a node, add it to the result, reduce in-degrees of neighbors; add any that become zero-in-degree to the queue.

DFS Post-Order Approach

Run DFS. When a node finishes (all neighbors processed), add it to a stack. The topological order is the reverse of the finish order.

Interview recognition: If the problem mentions "prerequisites," "dependencies," "build order," or "course schedule" — topological sort.

Dijkstra's Algorithm

Dijkstra's finds shortest paths from a source to all other vertices in a weighted graph with non-negative edge weights.

Implementation with Min-Heap

import heapq

def dijkstra(graph, start, n):
    dist = [float('inf')] * n
    dist[start] = 0
    heap = [(0, start)]  # (distance, node)
    
    while heap:
        d, node = heapq.heappop(heap)
        
        if d > dist[node]:
            continue  # outdated entry
            
        for neighbor, weight in graph[node]:
            new_dist = dist[node] + weight
            if new_dist < dist[neighbor]:
                dist[neighbor] = new_dist
                heapq.heappush(heap, (new_dist, neighbor))
    
    return dist

Complexity: O((V + E) log V) with a binary min-heap.

Union-Find (Disjoint Set Union)

Union-Find maintains a collection of disjoint sets and supports two operations efficiently: finding which set a node belongs to, and merging two sets.

Applications in Interviews

  • Detecting cycles in undirected graphs
  • Minimum spanning tree (Kruskal's algorithm)
  • Number of connected components
  • Dynamic connectivity problems

Implementation with Path Compression and Union by Rank

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * 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
        return True

Complexity: Near O(1) per operation with path compression and union by rank.

Common Graph Interview Problems by Algorithm

Problem Algorithm Key Insight
Number of Islands DFS/BFS Grid as implicit graph
Course Schedule (cycle detection) DFS coloring Directed graph cycle
Course Schedule II (ordering) Topological sort Kahn's or DFS post-order
Network Delay Time Dijkstra Weighted shortest path
Cheapest Flight Within K Stops Modified BFS/Bellman-Ford BFS with state (node, stops_remaining)
Pacific Atlantic Water Flow BFS/DFS from both borders Reverse direction thinking
Clone Graph BFS + hash map Track visited nodes with new nodes
Word Ladder BFS + neighbor generation State = current word
Redundant Connection Union-Find Find first edge that creates cycle
Accounts Merge Union-Find Group accounts by shared email

Frequently Asked Questions

When should I use BFS vs. DFS for graph problems? Use BFS when you need shortest paths, minimum steps, or level-by-level information. Use DFS when you need to explore all paths, detect cycles, perform topological ordering, or when the problem has a backtracking nature. For connected components and island counting, both work equally well — DFS is typically simpler to implement recursively.

Do I need to know Bellman-Ford and Floyd-Warshall for interviews? Bellman-Ford (handles negative edges) and Floyd-Warshall (all-pairs shortest path) are less commonly required but appear in some interview loops at top companies. Know the key properties: Bellman-Ford runs in O(VE) and can detect negative cycles; Floyd-Warshall runs in O(V³) and finds all-pairs shortest paths. If your interview is at a company known for hard algorithmic problems, study these.

How do I handle grid problems — are they graph problems? Yes. In grid problems, each cell is a node and edges exist between adjacent cells (typically 4-directional or 8-directional). BFS and DFS on grids follow the same patterns as on explicit graphs. The main difference is how you represent neighbors (using dx/dy offsets) rather than an adjacency list.

References

  1. Cormen, T. H., Leiserson, C. E., Rivest, R. L., & Stein, C. (2009). Introduction to Algorithms (3rd ed., Chapters 22-24). MIT Press.
  2. Sedgewick, R., & Wayne, K. (2011). Algorithms (4th ed., Part 4: Graphs). Addison-Wesley Professional.
  3. Skiena, S. S. (2008). The Algorithm Design Manual (2nd ed., Chapter 5). Springer.
  4. Dijkstra, E. W. (1959). A note on two problems in connexion with graphs. Numerische Mathematik, 1(1), 269-271.
  5. Gayle Laakmann McDowell. (2015). Cracking the Coding Interview (6th ed., Chapter 4: Trees and Graphs). CareerCup.