Reading
Matrix Boundaries and Transformations
Turn grid traversal into explicit shrinking boundaries and verify in-place transformations geometrically.
Learning objectives
- Represent an unvisited matrix region with four boundaries
- Avoid duplicate traversal when boundaries cross mid-layer
- Rotate a square matrix by transpose and row reversal
- Select edge cases that expose matrix-index bugs
A matrix is an array plus geometry
Matrix problems feel harder because two indices move at once. The cure is to name the geometry: which rectangle is still unprocessed, which edge is being consumed, and when that rectangle becomes empty.
Spiral traversal as a shrinking rectangle
Spiral Matrix (LeetCode #54) gives an m × n matrix and asks for its values in spiral
order: top row left-to-right, right side downward, bottom row right-to-left, left side
upward, then repeat inward.
Track inclusive boundaries:
top = 0 bottom = rows - 1
left = 0 right = columns - 1 After consuming an edge, immediately move its boundary inward:
function spiralOrder(matrix: number[][]): number[] {
if (matrix.length === 0 || matrix[0].length === 0) return []
const result: number[] = []
let top = 0
let bottom = matrix.length - 1
let left = 0
let right = matrix[0].length - 1
while (top <= bottom && left <= right) {
for (let column = left; column <= right; column += 1) {
result.push(matrix[top][column])
}
top += 1
for (let row = top; row <= bottom; row += 1) {
result.push(matrix[row][right])
}
right -= 1
if (top <= bottom) {
for (let column = right; column >= left; column -= 1) {
result.push(matrix[bottom][column])
}
bottom -= 1
}
if (left <= right) {
for (let row = bottom; row >= top; row -= 1) {
result.push(matrix[row][left])
}
left += 1
}
}
return result
} The two mid-loop checks are essential. In a single-row matrix, the top walk consumes
everything and moves top beyond bottom. Walking the bottom edge afterward would emit
the same row again. In a single-column matrix, the left-edge check prevents the same kind
of duplication.
Rotation as two understandable transformations
Rotate Image (LeetCode #48) asks you to rotate an n × n matrix 90 degrees clockwise
in place. Direct four-cell cycles work, but they are easy to index incorrectly. Compose
two simpler operations:
- Transpose across the main diagonal: swap
(row, column)with(column, row). - Reverse every row.
function rotateImage(matrix: number[][]): void {
const size = matrix.length
for (let row = 0; row < size; row += 1) {
for (let column = row + 1; column < size; column += 1) {
;[matrix[row][column], matrix[column][row]] =
[matrix[column][row], matrix[row][column]]
}
}
for (const row of matrix) row.reverse()
} Why start the inner loop at row + 1? Values on the diagonal map to themselves. Every
off-diagonal pair should be swapped exactly once. Iterating over the entire square would
swap each pair twice and undo the transpose.
Complexity and storage
Spiral traversal visits every cell once: O(rows · columns) time. Its result is required output; apart from that, four boundaries use O(1) auxiliary space. Rotation touches O(n²) cells and uses O(1) auxiliary space because each swap happens inside the matrix.
The matrix test set
Do not trust only a square 3×3 example. Trace these shapes:
- empty matrix, if the contract allows it;
- one cell;
- one row;
- one column;
- 2×2;
- more rows than columns;
- more columns than rows.
For rotation, the problem requires a square matrix. Say that constraint rather than silently applying the algorithm to a rectangle.
Common mistakes
- Mixing inclusive and exclusive boundaries.
- Shrinking a boundary before its edge is consumed.
- Omitting mid-layer checks and duplicating a row or column.
- Transposing every
(row, column)pair and swapping twice. - Using row count as column count for a rectangular matrix.
Draw the unvisited region
After one spiral layer, write the four new boundaries and point to the remaining rectangle. Then map each corner of a 3×3 matrix through transpose-plus-reverse.