Solve 12 classic graph interview problems — number of islands, clone graph, Pacific Atlantic water flow, number of provinces, word ladder, rotting oranges, critical connections, reconstruct itinerary, swim in rising water, alien dictionary, minimum height trees, and evaluate division.
Problem 1 — Number of Islands
def num_islands(grid):
"""Count connected components of '1's. DFS flood fill."""
if not grid: return 0
m, n = len(grid), len(grid[0])
count = 0
def dfs(r, c):
if r<0 or r>=m or c<0 or c>=n or grid[r][c]!='1': return
grid[r][c] = '#' # Mark visited
dfs(r+1,c); dfs(r-1,c); dfs(r,c+1); dfs(r,c-1)
for r in range(m):
for c in range(n):
if grid[r][c] == '1':
dfs(r, c); count += 1
return count
grid = [["1","1","0","0"],["1","1","0","0"],["0","0","1","0"],["0","0","0","1"]]
print(num_islands(grid)) # 3
Problem 3 — Pacific Atlantic Water Flow
def pacific_atlantic(heights):
"""
Find cells that can flow to BOTH Pacific and Atlantic oceans.
BFS from ocean borders inward.
"""
m, n = len(heights), len(heights[0])
from collections import deque
def bfs(starts):
visited = set(starts)
queue = deque(starts)
while queue:
r, c = queue.popleft()
for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)]:
nr, nc = r+dr, c+dc
if (0<=nr<m and 0<=nc<n and (nr,nc) not in visited
and heights[nr][nc] >= heights[r][c]):
visited.add((nr,nc)); queue.append((nr,nc))
return visited
pacific = [(0,c) for c in range(n)] + [(r,0) for r in range(1,m)]
atlantic = [(m-1,c) for c in range(n)] + [(r,n-1) for r in range(m-1)]
return list(bfs(pacific) & bfs(atlantic)) # Intersection
heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]]
print(len(pacific_atlantic(heights))) # Some cells reaching both oceans
Problem 4 — Number of Provinces (Connected Components)
def find_circle_num(is_connected):
"""Count friend circles (connected components) in adjacency matrix."""
n = len(is_connected)
visited = set()
count = 0
def dfs(i):
for j in range(n):
if is_connected[i][j] == 1 and j not in visited:
visited.add(j); dfs(j)
for i in range(n):
if i not in visited:
visited.add(i); dfs(i); count += 1
return count
print(find_circle_num([[1,1,0],[1,1,0],[0,0,1]])) # 2
print(find_circle_num([[1,0,0],[0,1,0],[0,0,1]])) # 3
from collections import deque
def word_ladder(beginWord, endWord, wordList):
"""Shortest transformation sequence — BFS on implicit graph."""
word_set = set(wordList)
if endWord not in word_set: return 0
queue = deque([(beginWord, 1)])
visited = {beginWord}
while queue:
word, length = queue.popleft()
for i in range(len(word)):
for c in 'abcdefghijklmnopqrstuvwxyz':
new_word = word[:i] + c + word[i+1:]
if new_word == endWord: return length + 1
if new_word in word_set and new_word not in visited:
visited.add(new_word); queue.append((new_word, length+1))
return 0
print(word_ladder("hit","cog",["hot","dot","dog","lot","log","cog"])) # 5
Problem 6 — Rotting Oranges (Multi-Source BFS)
def oranges_rotting(grid):
"""Multi-source BFS from all rotten oranges."""
from collections import deque
m, n = len(grid), len(grid[0])
queue = deque()
fresh = 0
for r in range(m):
for c in range(n):
if grid[r][c] == 2: queue.append((r,c,0))
elif grid[r][c] == 1: fresh += 1
if fresh == 0: return 0
max_time = 0
while queue:
r, c, time = queue.popleft()
for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)]:
nr, nc = r+dr, c+dc
if 0<=nr<m and 0<=nc<n and grid[nr][nc]==1:
grid[nr][nc] = 2; fresh -= 1
max_time = max(max_time, time+1)
queue.append((nr,nc,time+1))
return max_time if fresh == 0 else -1
print(oranges_rotting([[2,1,1],[1,1,0],[0,1,1]])) # 4
print(oranges_rotting([[2,1,1],[0,1,1],[1,0,1]])) # -1
Problem 7 — Critical Connections (Bridges)
def critical_connections(n, connections):
"""Find all bridges using Tarjan's algorithm."""
graph = [[] for _ in range(n)]
for u, v in connections:
graph[u].append(v); graph[v].append(u)
disc = [-1] * n; low = [-1] * n
timer = [0]; bridges = []
def dfs(u, parent):
disc[u] = low[u] = timer[0]; timer[0] += 1
for v in graph[u]:
if disc[v] == -1:
dfs(v, u)
low[u] = min(low[u], low[v])
if low[v] > disc[u]: # Bridge condition
bridges.append([u, v])
elif v != parent:
low[u] = min(low[u], disc[v])
dfs(0, -1)
return bridges
print(critical_connections(4, [[0,1],[1,2],[2,0],[1,3]])) # [[1,3]]
Problem 8 — Reconstruct Itinerary
def find_itinerary(tickets):
"""
Reconstruct itinerary using Hierholzer's algorithm (Eulerian path).
"""
from collections import defaultdict
graph = defaultdict(list)
for src, dst in sorted(tickets, reverse=True):
graph[src].append(dst)
result = []
def dfs(airport):
while graph[airport]:
dfs(graph[airport].pop())
result.append(airport)
dfs("JFK")
return result[::-1]
print(find_itinerary([["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]]))
# ["JFK","MUC","LHR","SFO","SJC"]
Problem 9 — Minimum Height Trees
def find_min_height_trees(n, edges):
"""
Find root(s) that minimize tree height.
Prune leaf nodes iteratively — roots are always the center.
Time: O(V)
"""
if n == 1: return [0]
from collections import deque, defaultdict
adj = defaultdict(set)
for u, v in edges:
adj[u].add(v); adj[v].add(u)
leaves = deque(i for i in range(n) if len(adj[i]) == 1)
remaining = n
while remaining > 2:
remaining -= len(leaves)
new_leaves = deque()
while leaves:
leaf = leaves.popleft()
neighbor = adj[leaf].pop()
adj[neighbor].remove(leaf)
if len(adj[neighbor]) == 1:
new_leaves.append(neighbor)
leaves = new_leaves
return list(leaves)
print(find_min_height_trees(4, [[1,0],[1,2],[1,3]])) # [1]
print(find_min_height_trees(6, [[3,0],[3,1],[3,2],[3,4],[5,4]])) # [3,4]
Problem 10 — Alien Dictionary (Topological Sort)
def alien_order(words):
"""Determine character order from sorted alien dictionary."""
from collections import defaultdict, deque
order = defaultdict(set)
in_degree = {c: 0 for word in words for c in word}
for i in range(len(words)-1):
w1, w2 = words[i], words[i+1]
for c1, c2 in zip(w1, w2):
if c1 != c2:
if c2 not in order[c1]:
order[c1].add(c2); in_degree[c2] += 1
break
else:
if len(w1) > len(w2): return "" # Invalid
queue = deque(c for c in in_degree if in_degree[c] == 0)
result = []
while queue:
c = queue.popleft(); result.append(c)
for nc in order[c]:
in_degree[nc] -= 1
if in_degree[nc] == 0: queue.append(nc)
return "".join(result) if len(result) == len(in_degree) else ""
print(alien_order(["wrt","wrf","er","ett","rftt"])) # "wertf"
Problem 11 — Evaluate Division
def calc_equation(equations, values, queries):
"""
Build weighted graph, use DFS to find path products.
"""
from collections import defaultdict
graph = defaultdict(dict)
for (a, b), val in zip(equations, values):
graph[a][b] = val; graph[b][a] = 1/val
def dfs(src, dst, visited):
if src not in graph or dst not in graph: return -1
if src == dst: return 1
visited.add(src)
for neighbor, weight in graph[src].items():
if neighbor not in visited:
result = dfs(neighbor, dst, visited)
if result != -1: return weight * result
return -1
return [dfs(a, b, set()) for a, b in queries]
print(calc_equation([["a","b"],["b","c"]],[2.0,3.0],[["a","c"],["b","a"]]))
# [6.0, 0.5]
Problem 12 — Swim in Rising Water
def swim_in_water(grid):
"""
Minimum time to swim from (0,0) to (n-1,n-1).
Dijkstra: cost to reach cell = max(elevation along path).
"""
import heapq
n = len(grid)
dist = [[float('inf')]*n for _ in range(n)]
dist[0][0] = grid[0][0]
pq = [(grid[0][0], 0, 0)]
while pq:
d, r, c = heapq.heappop(pq)
if r==n-1 and c==n-1: return d
for dr,dc in [(0,1),(0,-1),(1,0),(-1,0)]:
nr,nc=r+dr,c+dc
if 0<=nr<n and 0<=nc<n:
new_d = max(d, grid[nr][nc])
if new_d < dist[nr][nc]:
dist[nr][nc] = new_d
heapq.heappush(pq,(new_d,nr,nc))
return dist[n-1][n-1]
print(swim_in_water([[0,2],[1,3]])) # 3
Pattern Summary
| Problem | Pattern | Algorithm |
|---|
| Number of Islands | Connected components | DFS/BFS flood fill |
| Clone Graph | Graph traversal + copy | BFS with hashmap |
| Pacific Atlantic | Multi-source reachability | BFS from both oceans |
| Word Ladder | Shortest path implicit graph | BFS |
| Rotting Oranges | Multi-source BFS | BFS with timestamps |
| Critical Connections | Bridge finding | Tarjan's DFS |
| Reconstruct Itinerary | Eulerian path | Hierholzer's |
| Min Height Trees | Tree center | Leaf pruning |
| Alien Dictionary | Topological sort | Kahn's BFS |
| Evaluate Division | Path product | DFS with weights |
| Swim in Water | Min bottleneck path | Dijkstra (max instead of sum) |
*Graphs section complete.*