Before learning the patterns, let's quickly review the fundamentals.
A graph is a collection of nodes (vertices) connected by edges.
Alice ---- Bob
| |
Charlie ---- DavidMost graph problems ask you to explore connections, find paths, or optimize routes.
| Term | Meaning |
|---|---|
| Node (Vertex) | A point in the graph |
| Edge | Connection between two nodes |
| Path | Sequence of connected nodes |
| Cycle | A path that starts and ends at the same node |
| Connected Component | A group of reachable nodes |
Edges work in both directions.
A ----- BExamples:
Edges have a direction.
A -----> BExamples:
Every edge has a cost.
A --5--> BExamples:
Every edge has the same cost (usually 1).
Examples:
| Tree | Graph |
|---|---|
| No cycles | May contain cycles |
| Always connected | May be disconnected |
N nodes → N-1 edges | No fixed rule |
| One unique path | Multiple paths possible |
O(V²)adj = [[] for _ in range(n)]
for u, v in edges:
adj[u].append(v)
adj[v].append(u)O(V + E)| Operation | Complexity |
|---|---|
| DFS | O(V + E) |
| BFS | O(V + E) |
| Adjacency List | O(V + E) |
| Adjacency Matrix | O(V²) |
Use this pattern when you need to explore or visit nodes.
Look for phrases like:
If you see these clues, think DFS or BFS.
| DFS | BFS |
|---|---|
| Go as deep as possible | Explore level by level |
| Uses Stack / Recursion | Uses Queue |
| Great for traversal & backtracking | Great for shortest path (Unweighted Graph) |
| Algorithm | Time | Space |
|---|---|---|
| DFS | O(V + E) | O(V) |
| BFS | O(V + E) | O(V) |
def dfs(node):
visited.add(node)
for nei in graph[node]:
if nei not in visited:
dfs(nei)from collections import deque
q = deque([start])
visited = {start}
while q:
node = q.popleft()
for nei in graph[node]:
if nei not in visited:
visited.add(nei)
q.append(nei)Use this pattern when you need to count or identify disconnected groups.
Look for phrases like:
If you see these clues, think Connected Components.
| Algorithm | Time | Space |
|---|---|---|
| DFS | O(V + E) | O(V) |
| BFS | O(V + E) | O(V) |
visited = set()
components = 0
for node in range(n):
if node not in visited:
components += 1
dfs(node)Use this pattern when you need to find the minimum distance, cost, or number of moves.
Look for phrases like:
If you see these clues, think Shortest Path.
| Problem Type | Algorithm |
|---|---|
| Unweighted Graph | BFS |
| Weighted Graph (Positive Weights) | Dijkstra |
| Weighted Graph (Negative Weights) | Bellman-Ford |
| Shortest Path Between Every Pair | Floyd-Warshall |
| Algorithm | Time |
|---|---|
| BFS | O(V + E) |
| Dijkstra (Heap) | O((V + E) log V) |
| Bellman-Ford | O(V × E) |
| Floyd-Warshall | O(V³) |
from collections import deque
q = deque([(start, 0)])
visited = {start}
while q:
node, dist = q.popleft()
if node == target:
return dist
for nei in graph[node]:
if nei not in visited:
visited.add(nei)
q.append((nei, dist + 1))import heapq
pq = [(0, start)]
distance = {start: 0}
while pq:
dist, node = heapq.heappop(pq)
if dist > distance[node]:
continue
for nei, wt in graph[node]:
new_dist = dist + wt
if nei not in distance or new_dist < distance[nei]:
distance[nei] = new_dist
heapq.heappush(pq, (new_dist, nei))distance = [float('inf')] * n
distance[src] = 0
for _ in range(n - 1):
for u, v, wt in edges:
if distance[u] != float('inf') and distance[u] + wt < distance[v]:
distance[v] = distance[u] + wtINF = float('inf')
dist = [[INF] * n for _ in range(n)]
for i in range(n):
dist[i][i] = 0
for u, v, wt in edges:
dist[u][v] = wt
for via in range(n):
for i in range(n):
for j in range(n):
dist[i][j] = min(
dist[i][j],
dist[i][via] + dist[via][j]
)Use this pattern when tasks must be completed in a valid order based on dependencies.
Look for phrases like:
If you see these clues, think Topological Sort.
| Algorithm | Best For |
|---|---|
| Kahn's Algorithm (BFS) | Finding a valid ordering |
| DFS Topological Sort | Ordering using postorder traversal |
Note: Topological Sort only works on Directed Acyclic Graphs (DAGs).
| Algorithm | Time | Space |
|---|---|---|
| Kahn's Algorithm | O(V + E) | O(V) |
| DFS Topological Sort | O(V + E) | O(V) |
from collections import deque
indegree = [0] * n
for u in range(n):
for v in graph[u]:
indegree[v] += 1
q = deque()
for i in range(n):
if indegree[i] == 0:
q.append(i)
order = []
while q:
node = q.popleft()
order.append(node)
for nei in graph[node]:
indegree[nei] -= 1
if indegree[nei] == 0:
q.append(nei)visited = set()
order = []
def dfs(node):
visited.add(node)
for nei in graph[node]:
if nei not in visited:
dfs(nei)
order.append(node)
for node in range(n):
if node not in visited:
dfs(node)
order.reverse()Use this pattern when you need to connect all nodes with the minimum total cost or efficiently manage connected components.
Look for phrases like:
If you see these clues, think MST or Union-Find (DSU).
| Problem Type | Algorithm |
|---|---|
| Minimum Cost to Connect All Nodes | Kruskal's Algorithm |
| Dense Graph MST | Prim's Algorithm |
| Dynamic Connectivity | Union-Find (DSU) |
| Cycle Detection | Union-Find (DSU) |
| Algorithm | Time |
|---|---|
| Union-Find (Path Compression + Union by Rank) | O(α(N)) |
| Kruskal's Algorithm | O(E log E) |
| Prim's Algorithm (Heap) | O((V + E) log V) |
Note:
α(N)(Inverse Ackermann Function) grows so slowly that it's practically constant.
parent = list(range(n))
def find(x):
if parent[x] != x:
parent[x] = find(parent[x])
return parent[x]
def union(x, y):
px, py = find(x), find(y)
if px != py:
parent[py] = pxedges.sort(key=lambda x: x[2])
cost = 0
for u, v, wt in edges:
if find(u) != find(v):
union(u, v)
cost += wtimport heapq
visited = set()
pq = [(0, 0)] # (weight, node)
cost = 0
while pq:
wt, node = heapq.heappop(pq)
if node in visited:
continue
visited.add(node)
cost += wt
for nei, w in graph[node]:
if nei not in visited:
heapq.heappush(pq, (w, nei))| If the problem asks... | Think... | Algorithm |
|---|---|---|
| Explore or visit nodes | Graph Traversal | DFS / BFS |
| Count groups or islands | Connected Components | DFS / BFS |
| Minimum distance or cost | Shortest Path | BFS / Dijkstra / Bellman-Ford / Floyd-Warshall |
| Order tasks with dependencies | Topological Sort | Kahn's Algorithm / DFS |
| Connect everything with minimum cost | Minimum Spanning Tree | Kruskal / Prim |
| Merge sets or detect cycles | Union-Find | DSU |
## The Decision Tree
Graph Problem?
│
├── Need to explore?
│ → DFS / BFS
├── Need shortest path?
│ → BFS / Dijkstra
├── Need task ordering?
│ → Topological Sort
├── Need minimum cost to connect?
│ → MST
└── Need to merge/check components?
→ Union FindGraph problems can seem overwhelming because there are many algorithms to learn.
Instead of memorizing every algorithm, focus on recognizing the pattern first. Once you identify the pattern, choosing the right algorithm becomes much easier.
If this guide helped you, consider giving it an upvote so it can help more people preparing for coding interviews.
Happy Coding!