Problem Walkthrough

Spiral Matrix II LeetCode 59 Guide

Master LeetCode 59 Spiral Matrix II by filling an n x n matrix in spiral order using four boundary pointers — top, bottom, left, right — that shrink inward after each directional pass, incrementing a counter from 1 to n^2 to place every element in its correct spiral position.

7 min read|

Spiral Matrix II

LeetCode 59

Problem Overview — Generate n x n Matrix Filled 1 to n² in Spiral Order

LeetCode 59 Spiral Matrix II gives you a positive integer n and asks you to generate an n x n matrix filled with the elements 1 through n² in spiral order. Unlike Spiral Matrix I (LeetCode 54) which reads an existing matrix in spiral order, this problem asks you to write values into a freshly initialized matrix. The output is a 2D list of integers arranged in a clockwise spiral starting from the top-left corner.

The spiral order begins at position [0][0] and proceeds right across the top row, then down the right column, then left across the bottom row, then up the left column, and then inward to repeat the pattern for the next layer. Values start at 1 and increment by 1 at each step until n² is placed in the center cell (or final cell for odd n, which is the true center).

The problem is a clean construction exercise: there is no ambiguity about the spiral direction or starting point, and the matrix is always square. The key challenge is correctly tracking which cells have been filled and ensuring the boundary conditions stop the fill loop at exactly the right moment without skipping or double-filling any cell.

  • 1 <= n <= 20
  • Output is an n x n matrix filled with integers 1 through n²
  • Spiral order: clockwise starting from top-left corner
  • All n² cells must be filled — no cell is left empty
  • For odd n, the center cell gets the value n²
  • Return type is a 2D integer array (list of lists in Python)

Boundary Pointer Strategy — Four Variables Shrink Inward After Each Pass

The boundary-shrinking strategy uses four integer variables: top (starting row index), bottom (ending row index), left (starting column index), and right (ending column index). Initially top = 0, bottom = n-1, left = 0, right = n-1 — covering the entire matrix. A counter starts at 1 and increments each time a cell is filled.

Each iteration of the main loop fills one complete layer of the spiral. First, traverse the top row from left to right (columns left to right at row top), then increment top to shrink the boundary inward. Second, traverse the right column from top to bottom (rows top to bottom at column right), then decrement right. Third, traverse the bottom row from right to left (columns right to left at row bottom), then decrement bottom. Fourth, traverse the left column from bottom to top (rows bottom to top at column left), then increment left.

The loop continues as long as top <= bottom AND left <= right. When these conditions fail, all cells have been filled. For a 3x3 matrix this requires two iterations: the outer ring (8 cells, values 1–8) and the center cell (value 9). For a 1x1 matrix, a single fill covers the lone cell. The boundary conditions handle all sizes correctly without special cases.

💡

Writing Spiral vs. Reading Spiral — Same Technique, Reversed Role

This is the reverse of Spiral Matrix I: instead of reading existing values in spiral order, you write incrementing values in spiral order. The exact same four-pointer boundary-shrinking technique applies — top, bottom, left, right shrinking inward after each directional pass. If you already know Spiral Matrix I, Spiral Matrix II is simply replacing "read matrix[r][c]" with "matrix[r][c] = counter; counter++".

Filling Each Layer — Four Directional Passes with Boundary Updates

Each layer of the spiral consists of four directional segments. The first segment fills the top row from column left to column right (inclusive) at row index top, then increments top to move the top boundary one row inward. The second segment fills the right column from row top (already incremented) to row bottom (inclusive) at column index right, then decrements right.

The third segment fills the bottom row from column right (already decremented) to column left (inclusive), moving right to left at row index bottom, then decrements bottom. The fourth segment fills the left column from row bottom (already decremented) to row top (inclusive), moving bottom to top at column index left, then increments left. After all four segments and their boundary updates, the next iteration begins on the inner layer.

The order of operations — fill then update boundary — is critical. Each boundary update must happen immediately after the corresponding segment finishes, so the next segment starts from the correct updated boundary. Delaying boundary updates or batching them at the end of each iteration would cause off-by-one errors or double-filling of corner cells.

  1. 1Top row: for c in range(left, right+1): matrix[top][c] = counter++; then top++
  2. 2Right column: for r in range(top, bottom+1): matrix[r][right] = counter++; then right--
  3. 3Bottom row: for c in range(right, left-1, -1): matrix[bottom][c] = counter++; then bottom--
  4. 4Left column: for r in range(bottom, top-1, -1): matrix[r][left] = counter++; then left++
  5. 5Loop condition: while top <= bottom and left <= right
  6. 6Initialization: matrix = [[0]*n for _ in range(n)]; counter = 1

Termination — Loop Ends When Boundaries Cross After All n² Values Are Placed

The while loop terminates when top > bottom or left > right — meaning the boundaries have crossed and all cells are accounted for. For even n, the spiral completes after filling n/2 complete rings, and both conditions (top > bottom and left > right) become true simultaneously. For odd n, the spiral completes after (n-1)/2 full rings plus one final single-cell center fill.

The counter naturally increments from 1 to n² as each cell is filled. When the counter reaches n² + 1, the loop condition is already false and the loop has exited. There is no need to track the counter value to determine termination — the boundary conditions alone are sufficient. This makes the termination logic clean and independent of the counter.

Because we are generating a square matrix (n x n), the boundaries always narrow symmetrically. After each full ring, top and bottom each move one step closer to the center, and left and right each move one step closer to the center. This symmetric shrinking guarantees that the center cell (for odd n) is the last to be filled, receiving the value n².

ℹ️

No Early-Termination Checks Needed for Square Matrices

Unlike Spiral Matrix I where single-row or single-column edge cases require careful early-termination guards (to avoid re-traversing the last row or column), Spiral Matrix II always generates a square matrix. The boundaries naturally handle odd and even n without additional checks. The while top <= bottom and left <= right condition is the only termination guard needed — all four segments inside the loop execute correctly for any n >= 1.

Walk-Through for n=3 — Outer Ring 1–8, Center Cell 9

For n=3, initialize a 3x3 zero matrix and set top=0, bottom=2, left=0, right=2, counter=1. First iteration: top row (row 0, cols 0–2) fills positions [0][0]=1, [0][1]=2, [0][2]=3; top becomes 1. Right column (col 2, rows 1–2) fills [1][2]=4, [2][2]=5; right becomes 1. Bottom row (row 2, cols 1–0) fills [2][1]=6, [2][0]=7; bottom becomes 1. Left column (col 0, rows 1–1) fills [1][0]=8; left becomes 1.

After the first iteration the matrix looks like: [[1,2,3],[8,0,4],[7,6,5]] with the center cell still 0. Boundaries are now top=1, bottom=1, left=1, right=1 — a 1x1 inner region. Second iteration: top row (row 1, col 1) fills [1][1]=9; top becomes 2. Right column (col 1, rows 2–1) — rows 2 to 1 is empty since top (2) > bottom (1); no fill. Loop condition top(2) > bottom(1) exits.

Final matrix: [[1,2,3],[8,9,4],[7,6,5]]. This matches the expected LeetCode output for n=3. The center cell 9 is correctly placed as the last value. Note that the right column and bottom row passes in the second iteration are empty due to the boundary crossing — the loop exit condition catches this cleanly without producing incorrect fills.

  1. 1Init: matrix=[[0,0,0],[0,0,0],[0,0,0]], top=0, bottom=2, left=0, right=2, counter=1
  2. 2Iter 1 top row: [0][0]=1, [0][1]=2, [0][2]=3 → top=1
  3. 3Iter 1 right col: [1][2]=4, [2][2]=5 → right=1
  4. 4Iter 1 bottom row: [2][1]=6, [2][0]=7 → bottom=1
  5. 5Iter 1 left col: [1][0]=8 → left=1
  6. 6Iter 2: top=bottom=1, left=right=1 → [1][1]=9; boundaries cross; loop exits
  7. 7Result: [[1,2,3],[8,9,4],[7,6,5]]

Code Walkthrough — Python and Java with O(n²) Time and O(1) Extra Space

Python solution: def generateMatrix(n): matrix = [[0]*n for _ in range(n)]; top, bottom, left, right = 0, n-1, 0, n-1; counter = 1; while top <= bottom and left <= right: for c in range(left, right+1): matrix[top][c] = counter; counter += 1; top += 1; for r in range(top, bottom+1): matrix[r][right] = counter; counter += 1; right -= 1; for c in range(right, left-1, -1): matrix[bottom][c] = counter; counter += 1; bottom -= 1; for r in range(bottom, top-1, -1): matrix[r][left] = counter; counter += 1; left += 1; return matrix.

Java solution: public int[][] generateMatrix(int n) { int[][] matrix = new int[n][n]; int top = 0, bottom = n-1, left = 0, right = n-1, counter = 1; while (top <= bottom && left <= right) { for (int c = left; c <= right; c++) matrix[top][c] = counter++; top++; for (int r = top; r <= bottom; r++) matrix[r][right] = counter++; right--; for (int c = right; c >= left; c--) matrix[bottom][c] = counter++; bottom--; for (int r = bottom; r >= top; r--) matrix[r][left] = counter++; left++; } return matrix; } Both solutions are clean and handle all n from 1 to 20.

Time complexity: O(n²) — every cell in the n x n matrix is visited exactly once. Space complexity: O(1) extra space (the output matrix itself does not count as extra space per convention). The algorithm is in-place on the output buffer. No auxiliary data structures are needed — just four integer boundary variables and one counter.

⚠️

Use a Single Counter Variable — Do Not Compute Value from Position

Always use a single counter variable starting at 1 and incrementing with each cell fill. Do not try to compute the value at position [r][c] from the row and column indices — the formula is not straightforward and is extremely error-prone. The counter approach is O(1) per cell, just as simple, and trivially correct. A single counter is the idiomatic solution for any spiral fill problem.

Ready to master algorithm patterns?

YeetCode flashcards help you build pattern recognition through active recall and spaced repetition.

Start practicing now