Graph Patterns: The Matrix Traversal (Island) Pattern
When faced with 2D grid problems, constructing a traditional adjacency list is an unnecessary memory overhead. The 'Island Pattern' treats the grid itself as the graph, optimizing both space and traversal logic.
Introduction
Graph problems usually make people immediately think about:
- adjacency lists
- nodes
- edges
- complex graph construction
But 2D grid problems follow a completely different philosophy.
In matrix traversal problems:
The matrix itself IS the graph.
Every cell behaves like a graph node.
And neighboring cells automatically become edges.
Once you understand this mental shift, a massive category of graph problems suddenly becomes much easier.
This pattern is commonly called:
- Island Pattern
- Matrix Traversal Pattern
- Grid DFS/BFS Pattern
It appears everywhere in coding interviews.
Why Matrix Problems Are Secretly Graph Problems
Consider this grid:
text1 1 0 0 1 0 0 1 0 0 1 1
Each cell can connect to neighboring cells.
That means:
- cells = nodes
- movements = edges
You are already working with a graph.
The difference is:
The graph is implicit.
You do not need to build it manually.
The Biggest Beginner Mistake
Many beginners try converting the matrix into:
textMap<Integer, List<Integer>>
or some adjacency structure.
This creates:
- unnecessary memory usage
- extra preprocessing
- slower implementations
- complicated logic
In grid problems:
The matrix already stores everything you need.
Understanding Connectivity
In most island problems, movement is allowed in 4 directions:
textUP ↑ LEFT ← CELL → RIGHT ↓ DOWN
This creates natural graph connectivity.
Sometimes problems also allow diagonal movement:
text↖ ↑ ↗ ← X → ↙ ↓ ↘
Understanding movement directions is the foundation of the entire pattern.
The Direction Array Trick
One of the cleanest optimizations in matrix traversal is using a direction array.
Instead of writing:
javadfs(r - 1, c); dfs(r + 1, c); dfs(r, c - 1); dfs(r, c + 1);
we store movements in a reusable structure:
javaint[][] dirs = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
Now traversal becomes elegant:
javafor (int[] d : dirs) { dfs(r + d[0], c + d[1]); }
This dramatically improves readability and scalability.
The Core DFS Traversal Logic
Every matrix DFS follows the same structure:
Step 1: Boundary Validation
Before exploring a cell:
- ensure row is valid
- ensure column is valid
- ensure cell is traversable
- ensure cell is not already visited
Step 2: Mark Visited
Prevent infinite recursion loops.
Step 3: Explore Neighbors
Traverse all valid neighboring cells.
That is the entire pattern.
Visual DFS Walkthrough
Consider:
text1 1 0 1 0 1 0 1 1
Suppose we start DFS at:
text(0,0)
Step 1
Visit:
text1 1 0 1 0 1 0 1 1 ↑
Mark visited.
Step 2
Explore neighbors:
- up ❌
- down ✅
- left ❌
- right ✅
DFS spreads recursively.
Step 3
Entire connected island becomes visited.
This is exactly how:
- Number of Islands
- Flood Fill
- Surrounded Regions
- Distinct Islands
all work internally.
Why Infinite Loops Happen
Suppose you move:
textA → B
Without marking visited:
B again revisits A.
Then:
textA → B → A → B → A
Recursion never ends.
That is why visited tracking is mandatory.
The Elegant Optimization: In-Place Mutation
Most beginners use:
javaboolean[][] visited
This works.
But consumes:
textO(N × M)
extra memory.
Instead:
If the problem allows modifying input:
We can mutate the grid directly.
Example:
javagrid[r][c] = 0;
This instantly marks the node visited.
No extra matrix needed.
This is one of the cleanest space optimizations in graph traversal.
DFS vs BFS in Grid Problems
Both approaches work.
DFS (Depth First Search)
DFS explores deeply first.
Behavior:
textGo deep → backtrack → continue
Usually implemented recursively.
Best for:
- islands
- component counting
- shape exploration
BFS (Breadth First Search)
BFS explores level-by-level.
Behavior:
textExplore all neighbors first
Implemented using queue.
Best for:
- shortest path
- minimum distance
- nearest exits
- rotting oranges
Complexity Analysis
Suppose grid size is:
textN × M
Time Complexity
O(N × M)
Every cell is visited at most once.
Space Complexity
Recursive DFS
Worst case recursion stack:
textO(N × M)
BFS Queue
Worst case:
textO(N × M)
Auxiliary Space Optimization
If mutating grid directly:
No separate visited matrix required.
Common Matrix Traversal Problems
This exact pattern appears in:
- Number of Islands
- Flood Fill
- Rotten Oranges
- Pacific Atlantic Water Flow
- Surrounded Regions
- Number of Enclaves
- Distinct Islands
- Word Search
- Shortest Path in Binary Matrix
Mastering this single pattern unlocks a massive portion of graph interviews.
Common Mistakes
Forgetting Boundary Checks
Causes:
textArrayIndexOutOfBoundsException
Forgetting Visited Logic
Creates infinite recursion.
Building Adjacency Lists
Usually unnecessary for matrix problems.
Rewriting Neighbor Logic Repeatedly
Direction arrays simplify everything.
The Deep Insight Behind the Pattern
The real power of matrix traversal comes from understanding:
Graphs are not always explicit.
Sometimes:
The structure itself defines the graph naturally.
Once you recognize connectivity patterns inside matrices, graph traversal becomes dramatically simpler and far more intuitive.
Final Thoughts
The Matrix Traversal Pattern is one of the most important graph concepts in technical interviews.
At first, grid problems may appear visually intimidating.
But almost all of them reduce to three simple operations:
- validate
- mark visited
- explore neighbors
Once that recursive flow becomes natural, an enormous category of graph problems suddenly feels repetitive instead of difficult.
That is exactly when true pattern recognition begins.
Implementation
class Solution {
// 4-direction movement
int[][] dirs = {
{-1, 0}, // up
{1, 0}, // down
{0, -1}, // left
{0, 1} // right
};
public void dfs(int[][] grid, int r, int c) {
int rows = grid.length;
int cols = grid[0].length;
// Base condition
if (r < 0 || c < 0 || r >= rows || c >= cols || grid[r][c] == 0) {
return;
}
// Mark visited
grid[r][c] = 0;
// Explore neighbors
for (int[] d : dirs) {
dfs(grid, r + d[0], c + d[1]);
}
}
}