Problem
Given an m×n matrix, return all of its elements in spiral order — clockwise, starting from the top-left and winding inward.
Signal
"Visit a rectangular region in a fixed circular pattern" is a shrinking- boundary traversal — track four edges of the remaining region and peel off one side at a time, moving the corresponding boundary inward after each pass.
Approach
Maintain top, bottom, left, right boundaries. Walk right along the top
row, then down the right column, then left along the bottom row, then up the
left column — shrinking the matching boundary after each leg. The bottom-row
and left-column legs only run if a row or column actually remains between the
shrunk boundaries; skipping that check double-visits cells once the region
narrows to a single row or column. Stop when the boundaries cross.
Skeleton
top, bottom, left, right = 0, rows-1, 0, cols-1
result = []
while top <= bottom and left <= right:
walk right along row top; top += 1
walk down along column right; right -= 1
if top <= bottom:
walk left along row bottom; bottom -= 1
if left <= right:
walk up along column left; left += 1
Solution
def spiral_order(matrix: list[list[int]]) -> list[int]:
if not matrix or not matrix[0]:
return []
top, bottom = 0, len(matrix) - 1
left, right = 0, len(matrix[0]) - 1
result = []
while top <= bottom and left <= right:
for col in range(left, right + 1):
result.append(matrix[top][col])
top += 1
for row in range(top, bottom + 1):
result.append(matrix[row][right])
right -= 1
if top <= bottom:
for col in range(right, left - 1, -1):
result.append(matrix[bottom][col])
bottom -= 1
if left <= right:
for row in range(bottom, top - 1, -1):
result.append(matrix[row][left])
left += 1
return result
Complexity
O(rows · cols) time — every cell is visited exactly once. O(1) extra space, not counting the output list.
Pitfalls
- Skipping the
if top <= bottom/if left <= rightguards on the third and fourth legs re-visits a row or column once the remaining region is down to one row or one column. - Off-by-one range bounds are the easiest way to skip or duplicate a corner — trace a small 3×3 case by hand before trusting the loop bounds.
- This boundary-shrinking approach works for any m×n shape; a square-only trick like transpose-then-reverse (as in Rotate Image) does not generalize here.