Pattern #50
Graph DFS
Important interview questions, thinking patterns, Call Stack Deep Exploration, Directed 3-State Cycle Detection, Permanent vs Backtracking Unmarking, Grid DFS, and Go templates.
Must Solve
20 core questions — solve these first.
- 1.Number of IslandsGrid DFS: loop unvisited "1"s; trigger DFS to flood fill entire land mass to "0"medium
- 2.Clone GraphDFS + HashMap mapping original node -> cloned node; save clone in map BEFORE neighbor recursionmedium
- 3.Number of ProvincesAdjacency Matrix DFS: count unvisited node triggers to discover total connected componentsmedium
- 4.Max Area of IslandGrid DFS returning 1 + sum(dfs(neighbor)) for each connected land cellmedium
- 5.Flood Fill4-directional Grid DFS: change matching starting color cells to new color (check startColor != newColor)easy
- 6.Surrounded RegionsBoundary DFS: start DFS from border "O"s to mark safe cells; capture remaining internal "O"s to "X"medium
- 7.Pacific Atlantic Water FlowReverse DFS from Pacific and Atlantic borders inward; answer is cell intersection setmedium
- 8.Course Schedule3-State Directed Cycle DFS (0=unvisited, 1=visiting, 2=finished); encountering state 1 implies cycle deadlockmedium
- 9.Graph Valid TreeUndirected Cycle DFS: check edges == n - 1 AND 1 DFS from node 0 visits all n nodesmedium
- 10.Is Graph Bipartite?DFS 2-coloring: assign neighbor color = 1 - currColor; if neighbor has same color -> return falsemedium
- 11.Keys and RoomsDFS reachability starting from room 0 using visited boolean arraymedium
- 12.All Paths From Source to TargetDAG DFS + Backtracking: append node to path, recurse, then pop node after returnmedium
- 13.Reorder Routes to Make All Paths Lead to ZeroUndirected DFS from 0 tracking original direction vs artificial reverse edge costsmedium
- 14.Evaluate DivisionWeighted Directed Graph DFS: find product of edge weights along query path u -> vmedium
- 15.Detonate the Maximum BombsDirected DFS from each bomb u: edge u -> v exists if dist(u,v) <= radius(u)medium
- 16.Find Eventual Safe States3-State Directed Cycle DFS: terminal nodes and nodes leading only to safe nodes are safemedium
- 17.Possible BipartitionBuild dislike graph + DFS 2-coloringmedium
- 18.Accounts MergeEmail Graph DFS / Union Find: merge emails belonging to same accountmedium
- 19.Redundant ConnectionDFS Cycle Detection / Disjoint Set Union on undirected edge listmedium
- 20.Number of Connected ComponentsLoop all n nodes; if unvisited, increment count & run DFSmedium
Also Important
10 more questions worth practicing.
- 21.Path With Maximum GoldGrid DFS + Backtracking (mark cell 0 -> explore 4 directions -> restore original gold value)medium
- 22.Word SearchGrid DFS + Backtracking: match character, mark visited, recurse 4 directions, unmark cellmedium
- 23.Count Sub IslandsGrid DFS: island in grid2 is sub-island only if all its cells are land in grid1medium
- 24.Closed IslandsBoundary Grid DFS: eliminate land connected to grid borders, count remaining closed islandsmedium
- 25.Number of EnclavesBoundary Grid DFS: flood fill land connected to boundary, return count of remaining land cellsmedium
- 26.Coloring a BorderGrid DFS: color cell if it is on grid boundary or has an adjacent cell with different initial colormedium
- 27.Find if Path Exists in GraphBasic Graph DFS / BFS reachability from source to destinationeasy
- 28.Minimum Height TreesTree DFS / Kahn's leaf trimming to locate tree centersmedium
- 29.Longest Increasing Path in a MatrixGrid DFS + Memoization memo[r][c] storing longest strictly increasing path starting at cell (r,c)hard
- 30.Critical Connections in a NetworkTarjan's Bridge-Finding DFS algorithm using discovery time and lowest reachability arrayshard
How to Think
- Need explore one connected region?DFS (Go deep along paths)
- Need count connected groups?DFS + Visited Set
- Need detect cycles?DFS + 3-State Array (0=unvisited, 1=visiting, 2=finished)
- Need explore every possible path?DFS + Backtracking (Unmark upon return)
- Matrix has connected cells?Grid DFS (4-directional offsets)
Go Graph DFS Code Templates
Standard Graph DFS, 3-State Directed Cycle DFS & Grid DFS in Go
// 1. Standard Graph DFS (Connected Components): O(V + E) Time
func graphDFS(n int, adj [][]int) int {
visited := make([]bool, n)
components := 0
var dfs func(u int)
dfs = func(u int) {
visited[u] = true // Mark immediately upon entry!
for _, neighbor := range adj[u] {
if !visited[neighbor] {
dfs(neighbor)
}
}
}
for i := 0; i < n; i++ {
if !visited[i] {
components++
dfs(i)
}
}
return components
}
// 2. Directed Cycle Detection (Course Schedule 3-State DFS)
// States: 0 = Unvisited, 1 = Visiting (active DFS path), 2 = Finished
func canFinish(numCourses int, prerequisites [][]int) bool {
adj := make([][]int, numCourses)
for _, p := range prerequisites {
adj[p[1]] = append(adj[p[1]], p[0])
}
state := make([]int, numCourses)
var hasCycle func(u int) bool
hasCycle = func(u int) bool {
if state[u] == 1 { return true } // Found node in active DFS path -> Cycle!
if state[u] == 2 { return false } // Fully processed node -> Safe!
state[u] = 1 // Mark visiting
for _, neighbor := range adj[u] {
if hasCycle(neighbor) {
return true
}
}
state[u] = 2 // Mark finished
return false
}
for i := 0; i < numCourses; i++ {
if state[i] == 0 && hasCycle(i) {
return false // Cycle found -> Cannot finish all courses!
}
}
return true
}
// 3. Grid DFS (Number of Islands / Max Area): O(R · C) Time
func numIslands(grid [][]byte) int {
rows, cols := len(grid), len(grid[0])
islands := 0
var dfs func(r, c int)
dfs = func(r, c int) {
if r < 0 || r >= rows || c < 0 || c >= cols || grid[r][c] != '1' {
return
}
grid[r][c] = '0' // Sink island / mark visited!
dfs(r-1, c); dfs(r+1, c); dfs(r, c-1); dfs(r, c+1)
}
for r := 0; r < rows; r++ {
for c := 0; c < cols; c++ {
if grid[r][c] == '1' {
islands++
dfs(r, c)
}
}
}
return islands
}👉 Total Time: O(V + E) / O(R · C) | Stack Space: O(V) / O(R · C)
Cycle Detection States: Undirected vs Directed
1. Undirected Cycle Detection: Pass the parent node to the DFS call. If a neighbor is already visited AND neighbor != parent, a cycle exists!
2. Directed Cycle Detection (3-State Array): A simple boolean visited array is NOT enough for directed graphs! Use a 3-state array: 0 = unvisited, 1 = visiting (active in current call stack), 2 = finished. Reaching state 1 implies a back-edge directed cycle.
Permanent Visited vs Path Backtracking Unmarking
1. Permanent Visited (Component DFS): When counting connected components or island cells, mark visited[u] = true permanently. Do NOT unmark because connected nodes belong to the same component.
2. Temporary Visited (Path Backtracking): When exploring all paths (All Paths From Source to Target, Word Search), mark visited[u] = true before recursing, then UNMARK visited[u] = false after returning so the node can be reused in alternative paths!
Graph DFS → Go deep along paths using call stack LIFO structure
Visited Set → MANDATORY! Mark immediately upon entry (visited[u] = true)
Component Count → Outer loop over all nodes; new DFS trigger = 1 new component
Directed Cycle → 3-State Array (0=unvisited, 1=visiting, 2=finished)
Backtracking → Mark -> Recurse -> UNMARK (visited[u] = false)💡 Golden Rule: "Mark where you have been, follow one path as deep as possible, and only return when that path has nothing new left to explore."
Common Interview Mistakes
Omitting the visited set in a graph with cycles causes infinite recursion and stack overflow crashes!
In directed graphs, seeing a visited node from a previous independent branch is NOT a cycle! You must use a 3-state array (0, 1, 2).
In undirected graphs, the node you came from (parent) is already visited. Check neighbor != parent before declaring a cycle.
When enumerating all paths (Word Search, Path Max Gold), you MUST unmark visited[u] = false upon returning.
Interview Rules
- 1. Explore connected region / group? → Graph DFS
- 2. When to mark visited? → Mark
visited[u] = trueimmediately upon entry - 3. Count connected components?→ Outer loop over all nodes; if unvisited, increment count & trigger DFS
- 4. Directed Cycle Detection (Course Schedule)? → 3-State Array (
0=unvisited, 1=visiting, 2=finished) - 5. Undirected Cycle Detection? → Check
visited[neighbor] && neighbor != parent - 6. Explore all paths? → DFS + Backtracking (unmark node upon return)
- 7. Repeated subproblem work on Matrix/Graph? → DFS + Memoization (
memo[r][c]) - 8. Overall Complexity → Time:
O(V + E)| Stack Space:O(V)
Small Rules
- Rule 1: DFS goes deep along paths using call stack LIFO ordering.
- Rule 2: Mark nodes as visited immediately upon entering the function.
- Rule 3: 3-state tracking is required for directed graph cycle detection.
- Rule 4: Component DFS marks visited permanently; Backtracking unmarks nodes upon return.
- Rule 5: Grid DFS transforms 2D cell matrices into connected components.
Production Thinking
Microservice Dependency Inspection → Traversing full downstream microservice call graphs for impact analysis
Build System Cycle Detection → Detecting circular package dependencies using 3-state DFS prior to compilation
Network Component Reachability → Discovering all machines reachable from a compromised security gateway
File System Reference Trees → Recursively inspecting nested folder dependencies with cycle protection
Remember This
Graph DFS → Go Deep
Visited → Prevent cycles & repeats
Basic Pattern → Mark -> Explore neighbors
Components → New DFS = New Group
Grid → Cell = Node
All Paths → DFS + Backtracking
Undirected Cycle → Parent check
Directed Cycle → 3-State (0=unvisited, 1=visiting, 2=finished)
Clone Graph → DFS + Map
Repeated States → DFS + Memo
Time → O(V + E)💡 Golden Rule: "Mark where you have been, follow one path as deep as possible, and only return when that path has nothing new left to explore."