Spiral Matrix asks you to read a 2D grid in a spiral. There is no clever data structure here — the whole challenge is bookkeeping: tracking four moving boundaries cleanly so you visit every cell exactly once, in the right order.
Problem. Given an m x n matrix, return all of its elements in spiral order — starting at the
top-left, going right, then down, then left, then up, spiraling inward.
Example: matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] → answer [1, 2, 3, 6, 9, 8, 7, 4, 5].
The slow way first
You could try to "walk a robot" around the grid: keep a direction (right, down, left, up), step forward, and turn whenever you hit an edge or a cell you already visited. That works, but it needs a separate visited grid (extra O(m·n) space) and fiddly turn logic — easy to get an off-by-one wrong on the turns.
The question to ask: what stays simple as the spiral shrinks? The answer is the rectangle of unvisited cells. If I track its four edges, I never need a visited grid at all.
The idea: four shrinking boundaries
Keep four indices: top, bottom, left, right — the edges of the rectangle still to be read. Each pass peels one layer:
- Walk the top row left→right, then push
topdown. - Walk the right column top→bottom, then push
rightleft. - Walk the bottom row right→left, then push
bottomup. - Walk the left column bottom→top, then push
leftright.
Repeat while top <= bottom and left <= right. The box shrinks each loop until it is empty.
The two if guards (if top <= bottom, if left <= right) matter for non-square or odd-sized grids: after peeling the top and right, the box may already be empty, and we must not re-read a row or column.
Walk through it
Step through the animation on the 3x3 grid. Ring one peels 1, 2, 3 (top), 6, 9 (right), 8, 7 (bottom), 4 (left). Now all four boundaries collapse onto the center. One more loop reads the lone center cell 5, then top passes bottom and we stop — giving [1, 2, 3, 6, 9, 8, 7, 4, 5].
Pseudocode
result = empty list
top, bottom = 0, last row
left, right = 0, last column
while top <= bottom and left <= right:
walk left..right across row "top", append each # top row
top += 1
walk top..bottom down column "right", append each # right column
right -= 1
if top <= bottom: # bottom row (guard)
walk right..left across row "bottom", append each
bottom -= 1
if left <= right: # left column (guard)
walk bottom..top up column "left", append each
left += 1
return resultThe Python solution
def spiral_order(matrix):
result = []
top, bottom = 0, len(matrix) - 1
left, right = 0, len(matrix[0]) - 1
while top <= bottom and left <= right:
for c in range(left, right + 1):
result.append(matrix[top][c])
top += 1
for r in range(top, bottom + 1):
result.append(matrix[r][right])
right -= 1
if top <= bottom:
for c in range(right, left - 1, -1):
result.append(matrix[bottom][c])
bottom -= 1
if left <= right:
for r in range(bottom, top - 1, -1):
result.append(matrix[r][left])
left += 1
return resulttop,bottom,left,rightare the four edges of the rectangle that still needs reading.- The
whilecondition is the heart: as soon as the box is empty (boundaries cross), we stop. - Top row goes
left → right; right column goestop → bottom(notetopwas already bumped). - Bottom row goes
right → left(a reversedrangewith step-1); left column goesbottom → top. - The two
ifguards prevent re-reading a row or column once the box has shrunk to a single line.
Complexity
| Case | Time | Notes |
|---|---|---|
| Every cell visited once | O(m·n) (moderate) | m rows times n columns |
| Boundary bookkeeping | O(1) (fast) | four integer indices, no visited grid |
O(1) (fast)We touch each of the m·n cells exactly once, so time is O(m·n) — unavoidable, since the output lists every element. The boundary approach uses only O(1) extra space (ignoring the output list), beating the robot-walk that needs an O(m·n) visited grid.
When this pattern shows up
Whenever a matrix problem asks you to traverse in a layered or ring-by-ring order — spiral print, rotate an image in place, set matrix zeroes by border — think in terms of shrinking boundaries rather than a separate visited grid. Tracking edges as integers is both cheaper and less error-prone.
The two if guards are the classic bug. On a single-row or single-column leftover, skipping them makes
you re-read cells you already appended. Always re-check top <= bottom before the bottom row and
left <= right before the left column.
Practice
After ring one peels 1, 2, 3, 6, 9, 8, 7, 4 on the 3x3 grid, where do all four boundaries end up, and what is read next?
1. Why does the boundary approach not need a separate visited grid?
2. What is the purpose of the two if guards before the bottom row and left column?
3. In what order are the four edges peeled in each loop?
4. What is the time complexity of reading an m x n matrix in spiral order?