Skip to content

Coding patternsBlind 75

Spiral Matrix

Keep four walls around the part not yet visited, take one side, and move that wall in. Solved two ways, a walk with a visited grid and shrinking walls, both O(m·n) time, the second with O(1) extra space, with a step-through, code in five languages and the Big O worked out from the code.

You are given a grid matrix with m rows and n columns (rows and columns in the code). Return all its values in spiral order: start at the top left, go right along the top row, down the right column, left along the bottom row, up the left column, and keep circling inward until every value is taken.

Example 1

Input: matrix = [[7, 1, 4, 9], [3, 8, 2, 6], [5, 0, 10, 11]]

Output: [7, 1, 4, 9, 6, 11, 10, 0, 5, 3, 8, 2]

The outer ring gives 7, 1, 4, 9, 6, 11, 10, 0, 5, 3. The inner part, 8 and 2, comes last.

c0c1c2c3
r07149
r13826
r2501011
spiral
71496111005382

Example 2

Input: matrix = [[4, 6], [8, 2]]

Output: [4, 6, 2, 8]

Right along the top, down, then left along the bottom.

Example 3

Input: matrix = [[5], [6], [7]]

Output: [5, 6, 7]

One column: the spiral just goes down.

  • 1 ≤ m, n ≤ 10
  • −100 ≤ matrix[i][j] ≤ 100
Is the grid square?
Not always. m and n can differ, and a single row or column is allowed.
Which way does it turn?
Clockwise: right, down, left, up, and again.
May we change the grid?
Better not. Marking cells inside it would work, but it changes the caller's data.
What do we return?
A flat list of all m × n values.

This is a grid walk with a fixed route, so the work is pure bookkeeping. The route peels the grid like an onion: one ring, then the ring inside it. Four numbers, the walls top, bottom, left and right, describe what is left. Each side taken moves one wall in.

This problem has two approaches. Both take each cell once, and the second drops the visited grid.

Shrinking walls O(m·n), O(1) space

Keep four walls: top, bottom, left and right. Everything between them is not yet taken. Then go round:

  1. Take the top row from left to right. Move top down.
  2. Take the right column from top to bottom. Move right left.
  3. If a row is still left, take the bottom row from right to left. Move bottom up.
  4. If a column is still left, take the left column from bottom to top. Move left right.

Stop when the walls cross. The two if checks matter when only one row or one column is left: without them, the same cells would be taken twice.

The invariant: the cells between the walls are exactly the cells not yet taken.

c0c1c2c3
r07149
r13826
r2501011
result
empty
  1. top, bottom, left, right = 0, rows - 1, 0, columns - 1
  2. while top <= bottom and left <= right:
  3. for j in range(left, right + 1): result.append(matrix[top][j])
  4. top += 1
  5. for i in range(top, bottom + 1): result.append(matrix[i][right])
  6. right -= 1
  7. if top <= bottom:
  8. for j in range(right, left - 1, -1): result.append(matrix[bottom][j])
  9. bottom -= 1
  10. if left <= right:
  11. for i in range(bottom, top - 1, -1): result.append(matrix[i][left])
  12. left += 1
  13. return result

Four walls mark the part not yet taken: top, bottom, left and right. Take one side, then move that wall in by one.

1 / 8
Solution.javasolution.pySolution.cssolution.gosolution.ts
class Solution {
// Shrinking walls: take the top row, right column, bottom row and left column,
// moving each wall in after. Each cell once: O(m * n) time, O(1) extra memory.
public java.util.List<Integer> spiralOrder(int[][] matrix) {
int top = 0, bottom = matrix.length - 1, left = 0, right = matrix[0].length - 1;
java.util.List<Integer> result = new java.util.ArrayList<>();
// The cells between the walls are exactly the ones not taken yet.
while (top <= bottom && left <= right) {
for (int j = left; j <= right; j++) result.add(matrix[top][j]);
top++;
for (int i = top; i <= bottom; i++) result.add(matrix[i][right]);
right--;
// With one row left, the bottom row is the top row we just took.
if (top <= bottom) {
for (int j = right; j >= left; j--) result.add(matrix[bottom][j]);
bottom--;
}
// With one column left, the left column is the right one we just took.
if (left <= right) {
for (int i = bottom; i >= top; i--) result.add(matrix[i][left]);
left++;
}
}
return result;
}
}
Time O(m·n)Space O(1)Here is how we get these from the code, in four steps.
  1. 1 Find the work that repeats

    The four inner loops each take one cell per run. That is 1 step. The while loop goes once per ring. The question is how many cells all the inner loops take together.

    1. top, bottom, left, right = 0, rows - 1, 0, columns - 1
    2. while top <= bottom and left <= right:
    3. for j in range(left, right + 1): result.append(matrix[top][j])
    4. top += 1
    5. for i in range(top, bottom + 1): result.append(matrix[i][right])
    6. right -= 1
    7. if top <= bottom:
    8. for j in range(right, left - 1, -1): result.append(matrix[bottom][j])
    9. bottom -= 1
    10. if left <= right:
    11. for i in range(bottom, top - 1, -1): result.append(matrix[i][left])
    12. left += 1
    13. return result
  2. 2 Count the runs on Example 1

    Example 1 is 3 × 4. The sides take 4, 2, 3, 1 and then 2 cells.

    4 + 2 + 3 + 1 + 2 = 12 takes.

    sidetop row 0right column 3bottom row 2left column 0top row 1
    cells42312
    total
    4 + 2 + 3 + 1 + 2 = 12
  3. 3 Write the count for any n

    Every cell is taken exactly once, because each side moves its wall past the cells it took:

    m × n takes, plus a few wall moves per ring

    Check it with Example 1: 12 takes for 12 cells.

  4. 4 Keep only the part that grows fastest

    Drop the wall moves. There are at most about min(m, n) / 2 rings, far fewer than m·n cells. What is left is m·n.

    So the time is O(m·n).

  5. S Space: count the extra memory the same way

    The walls are four numbers, for any size of grid. The space is O(1), not counting the output list.

    topbottomleftright

2,000 random grids from 1 x 1 to 6 x 6, with single rows, single columns, tall grids and wide grids. Walk and turn and shrinking walls gave the same answer every time.

Largest inputsResult
Every shape up to 10 x 10 (the most allowed), 100 grids in all. Both approaches.The right order on every one.
A 10 x 10 grid. Shrinking walls.Under 0.001 s, and about 0.001 MB of extra memory, almost all of it the answer of 100 numbers. The walls are four numbers.