Graph Algorithms in Interviews: From Theory to Implementation
David Park
Product Manager
Graph algorithms are among the most feared - and most frequently tested - topics in technical interviews. From social network connections to GPS navigation, graphs underpin some of the most important systems in modern computing. Yet many candidates freeze when confronted with a graph problem, often because they never bridged the gap between textbook theory and practical implementation. This comprehensive guide will take you from foundational concepts to interview-ready code, covering BFS, DFS, shortest-path algorithms, topological sorting, and Union-Find - everything you need to tackle graph questions with confidence.
Why Graphs Matter in Technical Interviews
Graphs aren't just an academic curiosity - they model relationships in nearly every domain. Social networks are graphs. File systems are trees (a special graph). The internet itself is a massive directed graph. Interviewers love graph problems because they test a candidate's ability to think abstractly, choose the right traversal strategy, and handle edge cases like cycles, disconnected components, and weighted edges.
According to data from major interview platforms, graph-related questions appear in roughly25-30% of all coding interviews at FAANG companies. Yet candidates report feeling least prepared for these problems compared to arrays, strings, or even dynamic programming.
๐ Graph Questions by Company (2024-2025 Data)
Google averages 2-3 graph questions per interview loop. Meta focuses heavily on BFS/DFS in their coding rounds. Amazon frequently tests shortest-path and topological sort in system design contexts. Understanding graphs isn't optional - it's essential.
Before we dive into specific algorithms, let's make sure the fundamentals are solid. A graphG = (V, E) consists of a set of vertices (nodes) and a set ofedges (connections). Graphs can be directed or undirected, weighted or unweighted, cyclic or acyclic. Your choice of representation - adjacency list vs. adjacency matrix - has significant implications for time and space complexity.
| Representation | Space | Edge Lookup | Iterate Neighbors | Best For |
|---|---|---|---|---|
| Adjacency List | O(V + E) | O(degree) | O(degree) | Sparse graphs, most interview problems |
| Adjacency Matrix | O(Vยฒ) | O(1) | O(V) | Dense graphs, frequent edge queries |
| Edge List | O(E) | O(E) | O(E) | Kruskal's algorithm, simple storage |
"If you understand BFS and DFS deeply - really deeply - you can solve 80% of graph problems in interviews. Everything else is built on those two traversals."
- Gayle Laakmann McDowell, Author of Cracking the Coding Interview
BFS Deep Dive: Level-Order Thinking
Breadth-First Search (BFS) explores a graph layer by layer, visiting all neighbors of the current node before moving to the next level. It uses a queue (FIFO) data structure and is the go-to algorithm for finding the shortest path in unweighted graphs.
Core BFS Template
function bfs(graph, start) {
const visited = new Set();
const queue = [start];
visited.add(start);
while (queue.length > 0) {
const node = queue.shift();
// Process node here
for (const neighbor of graph[node]) {
if (!visited.has(neighbor)) {
visited.add(neighbor);
queue.push(neighbor);
}
}
}
}The key insight with BFS is that when you first reach a node, you've found the shortest path to it (in terms of number of edges). This property makes BFS ideal for problems like:
- โShortest path in unweighted graphs - BFS guarantees the minimum number of edges
- โLevel-order traversal - processing nodes layer by layer (e.g., binary tree levels)
- โMulti-source BFS - starting from multiple nodes simultaneously (e.g., "rotting oranges")
- โBipartite checking - 2-coloring a graph using BFS layers
- โWord ladder problems - finding shortest transformation sequences
Multi-Source BFS Example
A classic interview problem: given a grid where 1 represents land and 0 represents water, find the distance of each land cell to the nearest water cell. The trick is to start BFS from all water cells simultaneously:
function maxDistance(grid) {
const n = grid.length;
const queue = [];
// Add all water cells as starting points
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
if (grid[i][j] === 0) {
queue.push([i, j, 0]);
}
}
}
const dirs = [[0,1],[0,-1],[1,0],[-1,0]];
let maxDist = -1;
while (queue.length > 0) {
const [r, c, dist] = queue.shift();
for (const [dr, dc] of dirs) {
const nr = r + dr, nc = c + dc;
if (nr >= 0 && nr < n && nc >= 0 && nc < n
&& grid[nr][nc] === 1) {
grid[nr][nc] = 0; // mark visited
maxDist = Math.max(maxDist, dist + 1);
queue.push([nr, nc, dist + 1]);
}
}
}
return maxDist;
}DFS Patterns: The Recursive Powerhouse
Depth-First Search (DFS) explores as deep as possible along each branch before backtracking. It uses a stack (either explicitly or via recursion) and is incredibly versatile. While BFS is about breadth, DFS is about depth - and it excels at problems involving paths, cycles, connected components, and backtracking.
Recursive vs. Iterative DFS
// Recursive DFS
function dfsRecursive(graph, node, visited) {
visited.add(node);
// Process node here
for (const neighbor of graph[node]) {
if (!visited.has(neighbor)) {
dfsRecursive(graph, neighbor, visited);
}
}
}
// Iterative DFS
function dfsIterative(graph, start) {
const visited = new Set();
const stack = [start];
while (stack.length > 0) {
const node = stack.pop();
if (visited.has(node)) continue;
visited.add(node);
// Process node here
for (const neighbor of graph[node]) {
if (!visited.has(neighbor)) {
stack.push(neighbor);
}
}
}
}โก When to Use Iterative DFS
Use iterative DFS when the graph could be very deep (risk of stack overflow with recursion), when you need explicit control over the traversal order, or when the problem constraints indicate up to 10โต or more nodes. Python's default recursion limit is 1000, and even JavaScript can hit limits around 10,000-15,000 frames.
DFS Pattern: Cycle Detection in Directed Graphs
Detecting cycles is a classic DFS application. In a directed graph, you need three states: unvisited, in the current path (gray), and fully processed (black). A back edge to a gray node indicates a cycle:
function hasCycle(graph, n) {
const WHITE = 0, GRAY = 1, BLACK = 2;
const color = new Array(n).fill(WHITE);
function dfs(node) {
color[node] = GRAY;
for (const neighbor of graph[node]) {
if (color[neighbor] === GRAY) return true; // cycle!
if (color[neighbor] === WHITE && dfs(neighbor)) {
return true;
}
}
color[node] = BLACK;
return false;
}
for (let i = 0; i < n; i++) {
if (color[i] === WHITE && dfs(i)) return true;
}
return false;
}DFS Pattern: Connected Components
Counting connected components is another bread-and-butter DFS application. Simply iterate over all nodes, and each time you find an unvisited node, run DFS from it and increment your component count:
- โNumber of Islands - treat the grid as a graph, DFS from each unvisited land cell
- โFriend Circles - each DFS from an unvisited person discovers one friend group
- โAccounts Merge - find connected components among overlapping email sets
- โGraph Valid Tree - a tree is a connected acyclic graph with exactly V-1 edges
Shortest Path Algorithms
Once edges have weights, BFS alone won't cut it. You need algorithms specifically designed for weighted graphs. The two most important ones for interviews are Dijkstra's algorithm (non-negative weights) and Bellman-Ford (handles negative weights).
Dijkstra's Algorithm
Dijkstra's uses a priority queue (min-heap) to always process the closest unvisited node. It's greedy and requires all edge weights to be non-negative.
function dijkstra(graph, start, n) {
const dist = new Array(n).fill(Infinity);
dist[start] = 0;
// MinHeap: [distance, node]
const pq = new MinPriorityQueue();
pq.enqueue([0, start]);
while (!pq.isEmpty()) {
const [d, u] = pq.dequeue();
if (d > dist[u]) continue; // stale entry
for (const [v, weight] of graph[u]) {
const newDist = dist[u] + weight;
if (newDist < dist[v]) {
dist[v] = newDist;
pq.enqueue([newDist, v]);
}
}
}
return dist;
}| Algorithm | Time Complexity | Negative Weights? | Negative Cycles? | Use Case |
|---|---|---|---|---|
| BFS | O(V + E) | N/A (unweighted) | N/A | Unweighted shortest path |
| Dijkstra | O((V+E) log V) | โ No | โ No | Non-negative weighted graphs |
| Bellman-Ford | O(V ร E) | โ Yes | Detects them | Negative weights, cycle detection |
| Floyd-Warshall | O(Vยณ) | โ Yes | Detects them | All-pairs shortest path |
"In my 15 years of interviewing at Google, I've never seen a candidate fail because they didn't know Floyd-Warshall. But I've seen hundreds fail because they couldn't implement BFS or Dijkstra correctly under pressure."
- Alex Xu, Author of System Design Interview
Topological Sort: Ordering Dependencies
Topological sorting is a linear ordering of vertices in a Directed Acyclic Graph (DAG) such that for every directed edge u โ v, vertex u comes before v. It's essential for dependency resolution - think build systems, course prerequisites, or task scheduling.
Kahn's Algorithm (BFS-based)
Kahn's algorithm uses in-degree counting and a queue. It's intuitive and also detects cycles (if you can't process all nodes, there's a cycle):
function topologicalSort(graph, n) {
const inDegree = new Array(n).fill(0);
// Calculate in-degrees
for (let u = 0; u < n; u++) {
for (const v of graph[u]) {
inDegree[v]++;
}
}
// Start with all zero in-degree nodes
const queue = [];
for (let i = 0; i < n; i++) {
if (inDegree[i] === 0) queue.push(i);
}
const result = [];
while (queue.length > 0) {
const node = queue.shift();
result.push(node);
for (const neighbor of graph[node]) {
inDegree[neighbor]--;
if (inDegree[neighbor] === 0) {
queue.push(neighbor);
}
}
}
// If result doesn't contain all nodes, there's a cycle
return result.length === n ? result : [];
}๐ฏ Classic Topological Sort Interview Problems
- โข Course Schedule I & II - Can you finish all courses? In what order?
- โข Alien Dictionary - Derive letter ordering from sorted alien words
- โข Build Order - Determine project compilation order given dependencies
- โข Parallel Courses - Minimum semesters to complete all courses
- โข Sequence Reconstruction - Verify if a sequence is uniquely reconstructible
Union-Find (Disjoint Set Union)
Union-Find is a data structure that tracks elements partitioned into disjoint sets. It supports two primary operations: Find (which set does an element belong to?) and Union (merge two sets). With path compression and union by rank, both operations run in near-constant time - O(ฮฑ(n)), where ฮฑ is the inverse Ackermann function.
Optimized Union-Find Implementation
class UnionFind {
constructor(n) {
this.parent = Array.from({ length: n }, (_, i) => i);
this.rank = new Array(n).fill(0);
this.components = n;
}
find(x) {
if (this.parent[x] !== x) {
this.parent[x] = this.find(this.parent[x]); // path compression
}
return this.parent[x];
}
union(x, y) {
const px = this.find(x), py = this.find(y);
if (px === py) return false; // already connected
// union by rank
if (this.rank[px] < this.rank[py]) {
this.parent[px] = py;
} else if (this.rank[px] > this.rank[py]) {
this.parent[py] = px;
} else {
this.parent[py] = px;
this.rank[px]++;
}
this.components--;
return true;
}
connected(x, y) {
return this.find(x) === this.find(y);
}
}Union-Find shines in problems where you need to dynamically track connectivity. It's often the cleanest solution for problems that can also be solved with DFS/BFS but where you're processing edges incrementally:
- โNumber of Connected Components - process edges and count remaining components
- โRedundant Connection - find the edge that creates a cycle (the union that fails)
- โAccounts Merge - group accounts sharing common emails
- โKruskal's MST - sort edges by weight, union if not connected
- โEarliest Moment When Everyone Becomes Friends - process timestamps in order
๐ก BFS/DFS vs. Union-Find: When to Choose What
Use BFS/DFS when you need to traverse paths, compute distances, or the graph is given as an adjacency list. Use Union-Find when you're processing edges one at a time, need to check/merge components dynamically, or when the problem asks about connectivity after adding edges. Union-Find is also preferred when you need to track the number of components efficiently.
Practice Problems: Your Study Roadmap
Theory without practice is useless. Here's a curated list of problems organized by difficulty and technique. Start with the Easy/Medium problems to build pattern recognition, then graduate to the Hard ones.
| Problem | Difficulty | Technique | Key Insight |
|---|---|---|---|
| Number of Islands | ๐ข Medium | DFS/BFS | Grid as graph, sink visited cells |
| Clone Graph | ๐ข Medium | BFS/DFS + HashMap | Map old nodes to cloned nodes |
| Course Schedule II | ๐ก Medium | Topological Sort | Kahn's algorithm with in-degree |
| Network Delay Time | ๐ก Medium | Dijkstra | Find max of all shortest paths |
| Redundant Connection | ๐ก Medium | Union-Find | First edge where union fails |
| Word Ladder | ๐ด Hard | BFS | Each word is a node, BFS for shortest |
| Alien Dictionary | ๐ด Hard | Topological Sort | Build graph from adjacent word pairs |
| Cheapest Flights Within K Stops | ๐ด Hard | Modified Dijkstra / BFS | Track stops as extra state |
Study Strategy
- โWeek 1: Master BFS and DFS templates - solve 5 problems each
- โWeek 2: Tackle topological sort and cycle detection - 4 problems
- โWeek 3: Implement Dijkstra and Union-Find from scratch - 4 problems each
- โWeek 4: Mixed practice - time yourself, simulate interview conditions
The most important thing is to recognize which pattern a problem belongs to. Before writing any code, ask yourself: Is this a shortest-path problem? Does it involve connectivity? Are there dependencies that need ordering? The answer will point you to the right algorithm every time.
๐ง Quick Reference: Which Algorithm to Use
- โข "Shortest path" + unweighted โ BFS
- โข "Shortest path" + weighted (non-negative) โ Dijkstra
- โข "Shortest path" + negative weights โ Bellman-Ford
- โข "Find all paths" or "is there a path" โ DFS
- โข "Ordering" or "prerequisites" โ Topological Sort
- โข "Connected components" or "are X and Y connected" โ Union-Find or DFS
- โข "Cycle detection" โ DFS (directed) or Union-Find (undirected)
- โข "Minimum spanning tree" โ Kruskal's (Union-Find) or Prim's
Master Graph Algorithms with AI-Powered Practice
Stop memorizing - start understanding. Devana's AI interview coach walks you through graph problems step by step, identifies your weak patterns, and generates personalized practice sets. From BFS to Dijkstra, get interview-ready in weeks, not months.
Start Practicing Graph Problems โFree tier available ยท No credit card required ยท AI-powered feedback on every solution
More Articles
Time and Space Complexity in Interviews: What You Actually Get Asked
June 24, 2026 ยท 10 min read
From LeetCode Grinder to Interview-Ready: The Practice Method Nobody Talks About
June 18, 2025 ยท 14 min read
The System Design Interview Is Not About Systems: It's About How You Think
February 18, 2026 ยท 12 min read