Pattern 20 of 20
Matrix
Index arithmetic on grids: layer-by-layer traversal, in-place updates and prefix sums.
When to use it
- Rotating, transposing or spiralling a 2-D array
- Marking rows and columns without extra memory
- Repeated rectangle sums over the same grid
The idea
Treat the grid as rows and columns of indices and get the boundaries right. In-place tricks reuse the first row or column as storage, and a 2-D prefix sum answers any rectangle in O(1).
Typical cost O(m·n) time, O(1) to O(m·n) extra space.
How it works
A matrix problem is an array problem with two coordinates. Most of the difficulty is bookkeeping: staying inside the bounds, visiting cells in the right order, and not overwriting a value you still need.
Keep a direction list such as [(0, 1), (1, 0), (0, -1), (-1, 0)] and loop over it instead of writing four near-identical blocks. Pair it with a single in-bounds check. Spiral traversals cycle through the same list, turning when they hit a boundary.
In-place transformations rely on decomposing the operation. A 90-degree clockwise rotation is a transpose followed by reversing each row. Setting rows and columns to zero in O(1) extra space uses the first row and first column as marker storage.
When cells are nodes and neighbors are edges, the matrix is a graph: flood fill, islands and shortest paths through a maze are graph traversals. Sorted matrices add a search angle: starting from the top-right corner, each comparison eliminates a row or a column.
How to attack it
- 1Write rows and cols once and a single in-bounds helper.
- 2Use a direction list instead of repeated neighbor code.
- 3Decide whether you need a copy or can transform in place, and in which order.
- 4Treat cells as graph nodes when the question is about connectivity or distance.
- 5Test with 1 × 1, a single row, a single column, and a non-square matrix.
Template
A skeleton to adapt, not a solution to paste. Write it from memory a few times.
DIRS = [(0, 1), (1, 0), (0, -1), (-1, 0)] # right, down, left, up
def spiral(matrix):
if not matrix:
return []
rows, cols = len(matrix), len(matrix[0])
seen = [[False] * cols for _ in range(rows)]
r = c = d = 0
out = []
for _ in range(rows * cols):
out.append(matrix[r][c])
seen[r][c] = True
nr, nc = r + DIRS[d][0], c + DIRS[d][1]
if not (0 <= nr < rows and 0 <= nc < cols) or seen[nr][nc]:
d = (d + 1) % 4 # turn clockwise
nr, nc = r + DIRS[d][0], c + DIRS[d][1]
r, c = nr, nc
return out
def rotate_clockwise(m):
# In place for a square matrix: transpose, then reverse each row.
n = len(m)
for i in range(n):
for j in range(i + 1, n):
m[i][j], m[j][i] = m[j][i], m[i][j]
for row in m:
row.reverse()Variants
- Spiral and diagonal traversal
- Walk with a direction list and turn at boundaries, or shrink four boundaries (top, bottom, left, right) after each side.
- In-place rotation
- Transpose then reverse rows for clockwise; reverse rows then transpose for counterclockwise.
- Marker storage
- Use the first row and column (plus one flag) to remember which rows and columns to zero, for O(1) extra space.
- Grid as a graph
- Flood fill, islands and shortest paths: DFS or BFS over the four (or eight) neighbors.
- Sorted matrix search
- Start at the top-right corner and move left or down, or binary search a fully sorted matrix as one flat array.
Pitfalls
- Swapping rows and columns in bounds checks on non-square input.
- Transposing the whole matrix with j starting at 0, which swaps every pair twice and undoes itself.
- Overwriting cells you still need to read when updating in place (use a copy or encode two states in one cell).
- Building rows in Python with [[0] * cols] * rows, which aliases every row.
- Forgetting the empty matrix or the empty first row.
What it costs
| Operation | Cost |
|---|---|
| Visit every cell | O(rows × cols) |
| In-place rotation | O(n²) time, O(1) space |
| Staircase search in a sorted matrix | O(rows + cols) |
| Binary search on a flattened sorted matrix | O(log(rows × cols)) |
25 Matrix problems
Easiest first. Problems marked Free can be solved without Pro.
- 1Swap Rows for ColumnsFreeEasy
- 2Sum of Both DiagonalsFreeEasy
- 3Count Below-Zero CellsFreeEasy
- 4Repack the GridEasy
- 5Constant Down Every DiagonalEasy
- 6Fullest Row in a Sorted Bit GridEasy
- 7Turn the Grid by Quarter TurnsEasy
- 8Soften the ImageEasy
- 9Read the Grid in a SpiralMedium
- 10Quarter-Turn the PhotoMedium
- 11Blank Out Rows and ColumnsMedium
- 12Zigzag Diagonal ReadoutMedium
- 13Wind Numbers Into a SpiralMedium
- 14Next Generation of the ColonyMedium
- 15Rectangle Totals on DemandMedium
- 16Finished Sudoku of Any SizeMedium
- 17Sort Every DiagonalMedium
- 18Neighbourhood TotalsMedium
- 19Largest Square Within BudgetMedium
- 20Rotate Every RingMedium
- 21Richest Block Under a CapHard
- 22Fewest Presses to Darken the PanelHard
- 23Longest Uphill TrailHard
- 24Count All-Lit RectanglesHard
- 25Tightest Frame Around the Ink BlotHard
Solve them in the browser, in Python or JavaScript.