Matrix / 2D Arrays
Navigate and transform 2D grids using structured traversal patterns — row/column iteration, spiral order, layer-by-layer rotation, and in-place modifications.
Learn & Reference
Understanding the Pattern
Matrix / 2D Arrays Pattern
Matrices (2D arrays) appear constantly in interviews. The key to solving matrix problems efficiently is recognizing which traversal pattern the problem requires and applying it systematically. Most matrix problems boil down to one of a handful of movement strategies.
Core Concept
A matrix is a 2D grid with m rows and n columns. You access element at row r, column c as matrix[r][c]. The four cardinal directions are (r-1,c), (r+1,c), (r,c-1), (r,c+1). Diagonals add four more: (r-1,c-1), (r-1,c+1), (r+1,c-1), (r+1,c+1).
The 5 Matrix Archetypes
1. Row-Major / Column-Major Traversal
The simplest pattern: iterate every cell in row order or column order. Used for search, counting, and set-zeroes marking.
2. Spiral Traversal
Walk the matrix in a spiral: right across the top row, down the right column, left across the bottom row, up the left column, then shrink the boundary inward and repeat. Track four boundaries: top, bottom, left, right.
3. Layer-by-Layer Processing (Rotate Image)
Process the matrix in concentric rectangular layers from outside to inside. Each layer has layer_size - 1 rotations. This is the key to in-place 90-degree rotation: for each position in a layer, do a 4-way swap.
4. Transpose + Reverse (Rotation Shortcut)
Rotate 90 degrees clockwise = transpose the matrix (swap matrix[i][j] with matrix[j][i]), then reverse each row. This is simpler to code than the 4-way swap and runs in O(m*n) time.
5. Binary Search on Sorted Matrix
When a matrix is sorted (each row sorted, first element of each row > last element of previous row), treat it as a flattened sorted array. Map index mid to (mid / n, mid % n) and run standard binary search.
Boundary Checking
Almost every matrix problem requires bounds checking. Before accessing matrix[r][c], verify:
0 <= r < rows0 <= c < cols
A common helper is a direction array: dirs = [(0,1),(0,-1),(1,0),(-1,0)] to iterate neighbors cleanly.
In-Place Modifications
Many problems require O(1) extra space. Common tricks:
- Use the matrix itself as storage: mark visited cells with a sentinel value (e.g., set to
0or-1) - First row/column as flags: for "set matrix zeroes", use the first row and column to record which rows/columns need zeroing
- Swap in cycles: for rotation, swap 4 elements at a time in a cycle
When Matrix Pattern Fails
- When the grid represents a graph with complex connectivity (use BFS/DFS instead)
- When you need to find shortest paths in a weighted grid (use Dijkstra)
- When the matrix is sparse and better represented as a hash map of coordinates
Common Mistakes
- Mixing up rows and columns:
matrix[row][col]notmatrix[col][row]. Rows =matrix.length, Cols =matrix[0].length - Off-by-one in spiral traversal: forgetting to update boundaries after each direction, or not checking
top <= bottomandleft <= rightbefore the return passes - Modifying while iterating: for set-zeroes, if you zero out cells immediately, you lose information about which cells were originally zero. Mark first, then zero
- Assuming square matrix: many problems have
m != n. Always use separate row and column counts - Forgetting edge cases: empty matrix, single row, single column, 1x1 matrix
- Wrong rotation direction: clockwise 90 = transpose + reverse rows. Counter-clockwise 90 = transpose + reverse columns (or reverse rows then transpose)
When to Use
Template
1# --- Matrix Traversal (Row-Major) ---2def traverse(matrix):3"""Visit every cell in row-major order"""4if not matrix or not matrix[0]:5return6m, n = len(matrix), len(matrix[0])7for r in range(m):8for c in range(n):9process(matrix[r][c])1011# --- Spiral Order Traversal ---12def spiral_order(matrix):13"""Return all elements in spiral order"""14result = []15if not matrix or not matrix[0]:16return result17top, bottom = 0, len(matrix) - 118left, right = 0, len(matrix[0]) - 11920while top <= bottom and left <= right:21# Traverse right across top row22for c in range(left, right + 1):23result.append(matrix[top][c])24top += 12526# Traverse down right column27for r in range(top, bottom + 1):28result.append(matrix[r][right])29right -= 13031# Traverse left across bottom row32if top <= bottom:33for c in range(right, left - 1, -1):34result.append(matrix[bottom][c])35bottom -= 13637# Traverse up left column38if left <= right:39for r in range(bottom, top - 1, -1):40result.append(matrix[r][left])41left += 14243return result4445# --- Rotate Matrix 90° Clockwise (In-Place) ---46def rotate(matrix):47"""Rotate n x n matrix clockwise: transpose then reverse rows"""48n = len(matrix)49# Step 1: Transpose50for i in range(n):51for j in range(i + 1, n):52matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]53# Step 2: Reverse each row54for row in matrix:55row.reverse()
Syntax Reference
1# === Matrix Creation ===2rows, cols = 3, 43matrix = [[0] * cols for _ in range(rows)] # 3x4 of zeros4# WRONG: [[0]*cols]*rows (all rows share same list!)56# === Dimensions ===7m = len(matrix) # rows8n = len(matrix[0]) # cols910# === Access ===11val = matrix[r][c] # get element12matrix[r][c] = val # set element1314# === Row-Major Traversal ===15for r in range(m):16for c in range(n):17process(matrix[r][c])1819# === Column-Major Traversal ===20for c in range(n):21for r in range(m):22process(matrix[r][c])2324# === 4-Directional Neighbors ===25dirs = [(0, 1), (0, -1), (1, 0), (-1, 0)]26for dr, dc in dirs:27nr, nc = r + dr, c + dc28if 0 <= nr < m and 0 <= nc < n:29process(matrix[nr][nc])3031# === Transpose (in-place, square only) ===32for i in range(len(matrix)):33for j in range(i + 1, len(matrix)):34matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]3536# === Reverse Each Row ===37for row in matrix:38row.reverse()
Common Recipes
1# --- Spiral Traversal ---2def spiral_order(matrix):3result = []4if not matrix:5return result6top, bottom = 0, len(matrix) - 17left, right = 0, len(matrix[0]) - 189while top <= bottom and left <= right:10# Right across top row11for c in range(left, right + 1):12result.append(matrix[top][c])13top += 11415# Down right column16for r in range(top, bottom + 1):17result.append(matrix[r][right])18right -= 11920# Left across bottom row21if top <= bottom:22for c in range(right, left - 1, -1):23result.append(matrix[bottom][c])24bottom -= 12526# Up left column27if left <= right:28for r in range(bottom, top - 1, -1):29result.append(matrix[r][left])30left += 13132return result3334# --- Rotate 90 Degrees Clockwise (In-Place) ---35def rotate(matrix):36n = len(matrix)37# Transpose38for i in range(n):39for j in range(i + 1, n):40matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]41# Reverse each row42for row in matrix:43row.reverse()4445# --- Set Matrix Zeroes (O(1) Space) ---46def set_zeroes(matrix):47m, n = len(matrix), len(matrix[0])48first_row_zero = any(matrix[0][c] == 0 for c in range(n))49first_col_zero = any(matrix[r][0] == 0 for r in range(m))5051# Use first row/col as flags52for r in range(1, m):53for c in range(1, n):54if matrix[r][c] == 0:55matrix[r][0] = 056matrix[0][c] = 05758# Zero cells based on flags59for r in range(1, m):60for c in range(1, n):61if matrix[r][0] == 0 or matrix[0][c] == 0:62matrix[r][c] = 06364# Handle first row and col65if first_row_zero:66for c in range(n):67matrix[0][c] = 068if first_col_zero:69for r in range(m):70matrix[r][0] = 0
Complexity
| Operation | Time | Space |
|---|---|---|
| Full traversal (row/col major) | O(m*n) | O(1) |
| Spiral traversal | O(m*n) | O(1) extra |
| Rotate 90 (transpose+reverse) | O(n^2) | O(1) |
| Search sorted matrix | O(log(m*n)) | O(1) |
| Set matrix zeroes | O(m*n) | O(1) |
| Element access | O(1) | O(1) |
Watch Out
- ✗When the grid represents a graph with complex connectivity (use BFS/DFS instead)
- ✗When you need to find shortest paths in a weighted grid (use Dijkstra)
- ✗When the matrix is sparse and better represented as a hash map of coordinates
- ✗Mixing up rows and columns: `matrix[row][col]` not `matrix[col][row]`. Rows = `matrix.length`, Cols = `matrix[0].length`
- ✗Off-by-one in spiral traversal: forgetting to update boundaries after each direction, or not checking `top <= bottom` and `left <= right` before the return passes
- ✗Modifying while iterating: for set-zeroes, if you zero out cells immediately, you lose information about which cells were originally zero. Mark first, then zero
- ✗Assuming square matrix: many problems have `m != n`. Always use separate row and column counts
- ✗Forgetting edge cases: empty matrix, single row, single column, 1x1 matrix
- ✗Wrong rotation direction: clockwise 90 = transpose + reverse rows. Counter-clockwise 90 = transpose + reverse columns (or reverse rows then transpose)