Graph Interview Problems - Topological Sort Cycle Bug
A single undetected cycle in a topological sort OOM-killed our build server.
- Graph problems appear in ~30% of FAANG coding rounds because real systems (Maps, Social Nets, Dispatch) are graphs under the hood.
- Core patterns: BFS for shortest paths (unweighted), DFS for cycles/connectivity, Topological Sort for dependencies, Union-Find for dynamic connectivity.
- Choosing the wrong traversal (DFS for shortest path) is the #1 mistake that costs an offer.
- Performance trap: Adjacency List (O(V+E) space) beats Adjacency Matrix (O(V²) space) for sparse interview graphs.
- Production insight: Forgetting to mark visited nodes causes infinite loops — same failure pattern in dependency resolution at scale.
Imagine a city map. Every intersection is a dot, and every road connecting two intersections is a line. A graph is exactly that — dots (nodes) connected by lines (edges). Graph problems ask you things like: 'What's the shortest route from home to school?', 'Can I get from any city to any other city?', or 'Is there a road that, if it breaks, cuts two towns off from each other?' Every GPS app, social network, and package delivery system solves graph problems thousands of times per second.
Graph problems are the great filter of software engineering interviews. They appear in roughly 30% of FAANG-level coding rounds not because interviewers love theory, but because real systems — Google Maps, LinkedIn's friend-of-a-friend suggestions, Uber's dispatch engine, Kubernetes dependency resolution — are fundamentally graphs under the hood. If you can't navigate a graph, you can't reason about the systems that run the modern internet.
The challenge with graph interviews isn't memorizing algorithms. BFS and DFS are straightforward once you've seen them twice. The real difficulty is pattern recognition: knowing within 60 seconds whether a problem is a shortest-path problem, a cycle detection problem, a topological sort problem, or a connected-components problem — and then executing flawlessly under pressure without missing the edge cases that separate a 'hire' from a 'no hire'.
By the end of this article you'll have a mental framework for classifying any graph problem on sight, complete runnable solutions for the 10 most common interview problems, and the internal mechanics — why BFS gives shortest paths in unweighted graphs, why DFS finds cycles, why Union-Find beats DFS for dynamic connectivity — explained at the level an interviewer expects when they ask 'can you walk me through your reasoning?'
Breadth-First Search (BFS): The Shortest Path Pattern
Breadth-First Search is the definitive algorithm for finding the shortest path in an unweighted graph. It explores the graph in 'layers'—visiting all neighbors at distance 1, then distance 2, and so on. This guarantees that the first time you reach a target node, you've found the shortest route. In interviews, this pattern is frequently disguised as 'minimum number of moves to solve a puzzle' or 'nearest exit in a maze.'
Here's how the BFS pattern works in practice: you maintain a queue and a visited set. Each layer corresponds to one iteration over the current queue size. For each node popped, you examine its neighbors. If a neighbor hasn't been visited, mark it visited and enqueue it. The distance increments only after processing a full layer. This gives you the correct minimum distance by construction.
A common variant is the 'multi-source BFS' where you start from several nodes simultaneously — used in problems like 'walls and gates' or 'spreading fire.'
Depth-First Search (DFS): Cycle Detection, Connectivity, and Exhaustive Paths
Depth-First Search explores as far as possible along each branch before backtracking. It's the algorithm of choice when you need to explore all possibilities — detecting cycles, finding connected components, or solving problems like 'clone a graph' or 'path exists between two nodes.' DFS can be implemented recursively (simpler) or iteratively with a stack (avoids stack overflow for deep graphs).
Cycle detection using DFS is a classic pattern. For undirected graphs, you need to check if you encounter a neighbor that is visited and is NOT the parent of the current node. For directed graphs, you also need a recursion stack (set) to detect a back edge — a node that is currently in the recursion stack means a cycle.
DFS also forms the backbone of backtracking problems on graphs, like finding all paths from source to target in a DAG. In those cases, you don't mark nodes as visited globally; instead, you mark them on the current path and unmark after recursion.
- White (0): node not visited yet.
- Gray (1): node is currently on the recursion stack (part of current path).
- Black (2): node and all descendants fully explored.
- If we encounter a gray node during DFS, a cycle exists.
Topological Sort: Handling Task Dependencies (Kahn's Algorithm)
Topological sorting is only applicable to Directed Acyclic Graphs (DAGs). It provides a linear ordering of vertices such that for every directed edge $u \to v$, vertex $u$ comes before $v$. This is the foundation of build systems (like Gradle or Maven) and task scheduling. Kahn's Algorithm is the preferred iterative approach using in-degrees.
The algorithm works by maintaining an in-degree array for each node. Initialize a queue with all nodes that have in-degree 0. Then repeatedly pop a node, append it to result, and for each neighbor, decrement its in-degree. If a neighbor's in-degree becomes 0, push it into the queue. If at the end the result size doesn't equal the number of nodes, the graph has a cycle.
The alternative is recursive topological sort using DFS with a list of nodes in post-order — reverse of finishing times gives the topological order.
Union-Find (Disjoint Set Union): Dynamic Connectivity & Redundant Edges
Union-Find is designed for problems where you need to efficiently merge sets and answer connectivity queries under dynamic edge additions. It excels in scenarios like 'find redundant connection' (detect where an edge connects two already-connected nodes) or 'number of components after adding edges online.'
The core operations are find() — with path compression — and union() — using union by rank or size. Optimized versions run in almost O(1) amortized time (inverse Ackermann function). Union-Find does not handle graph traversal or shortest paths; it's strictly for connectivity.
A classic interview problem: given a list of edges in a graph, find which edge creates a cycle (redundant connection). Using Union-Find, you process edges one by one. For each edge (u, v), if find(u) == find(v), the edge is redundant. Otherwise, union(u, v).
- find() climbs up the parent pointers to the root, compressing along the way.
- union() attaches the smaller tree root to the larger tree root (by rank).
- Time complexity: amortized O(α(n)) where α is the inverse Ackermann function.
- Useful for: detecting cycles in undirected graphs, number of islands (dynamic version), Kruskals MST algorithm.
Graph Representation & Pattern Recognition: The First 60 Seconds
The most critical skill in graph interviews is recognising the pattern within the first minute. Here's a quick decision tree:
- Need shortest path in unweighted graph? → BFS.
- Need all paths or detect cycles? → DFS.
- Need to process tasks in order? → topological sort (Kahn/DFS).
- Need dynamic connectivity or detect redundant edges? → Union-Find.
- Weighted graph, shortest path, no negative edges? → Dijkstra.
- Weighted graph with negative edges? → Bellman-Ford.
- Need all-pairs shortest paths? → Floyd-Warshall.
Graph representation also matters. Adjacency List (List<List<Integer>>) is the default for sparse graphs (V up to 10^5, E up to 10^5). Adjacency Matrix (int[][]) only if V is small (< 1000) or graph is dense (E ~ V²). Edge List (List<int[]>) is useful when you need to process edges without adjacency queries.
In interviews, you'll rarely need to implement Dijkstra from scratch — but you must be able to reason about its behavior and write BFS/DFS/Kahn without hesitation.
The Build Pipeline That Looped Forever
- A topological sort without cycle detection is not a topological sort — it's a ticking bomb.
- Every dependency resolution system must fail fast on cycles. Silent retries are dangerous.
- Add a dedicated cycle detection pass (DFS + recursion stack) before any DAG processing.
Key takeaways
Common mistakes to avoid
5 patternsUsing DFS to find shortest path in unweighted graph
Forgetting to mark nodes as visited
Not handling disconnected components
Using adjacency matrix for large sparse graphs (V > 10^4)
Confusing directed vs undirected cycle detection
Interview Questions on This Topic
Given a reference of a node in a connected undirected graph, return a deep copy (clone) of the graph. Explain your approach and write the code.
Frequently Asked Questions
That's Coding Patterns. Mark it forged?
4 min read · try the examples if you haven't