Skip to content
← All posts

Spiral Matrix III: Walking a Spiral from Any Starting Point

5 min read
leetcodeproblemmediumarraysmatrixsimulation

You are given a grid with rows rows and cols columns, along with a starting position (rStart, cStart). Your job is to walk outward in a clockwise spiral from that starting cell, collecting every cell that falls inside the grid, and return the coordinates of all rows * cols cells in the order you visit them.

This is LeetCode 885: Spiral Matrix III. Unlike Spiral Matrix I and II, you are not peeling layers off a filled grid or filling a grid inward. Instead, you start at an arbitrary position and spiral outward into open space. Some of the positions you walk through will land outside the grid boundaries, and you simply skip those.

c0c1c2c3r0r1r2678950110x43211xxxxxStartIn-boundsOut-of-bounds
Spiral walk from (1,1) on a 3x4 grid. Numbers show collection order. The dashed border marks the valid grid. Cells outside are visited but skipped.

Why this problem matters

Spiral Matrix III flips the perspective of the classic spiral traversal problems. In Spiral Matrix I and II, the grid boundaries define where you walk. Here, the spiral path is independent of the grid. You walk the spiral unconditionally and only record positions that happen to fall within bounds. This shift in framing turns a boundary-management problem into a simulation problem.

The technique you learn here, simulating a geometric path and filtering by a validity condition, appears in many other contexts. Robot simulations, game board traversals, and coordinate-based flood fills all share this "walk and check" structure. Once you internalize the spiral walk pattern, you can swap the validity check for any predicate and reuse the skeleton.

This problem also reinforces a useful mental model: sometimes the cleanest solution is to overshoot on purpose. Rather than carefully computing which cells to visit, you walk the full spiral and let the boundary check handle the rest. The code ends up shorter and less error-prone than trying to clip the spiral to the grid edges.

The spiral pattern

The key insight is that the outward spiral follows a repeating direction cycle with predictable side lengths. Starting from (rStart, cStart), you walk:

  1. East 1 step, then South 1 step (side length = 1)
  2. West 2 steps, then North 2 steps (side length = 2)
  3. East 3 steps, then South 3 steps (side length = 3)
  4. West 4 steps, then North 4 steps (side length = 4)
  5. And so on...

The direction sequence is always East, South, West, North, repeating. The side length increases by 1 after every two directions. So directions come in pairs that share the same length: (E, S) share length 1, then (W, N) share length 2, then (E, S) share length 3, and so forth.

At each position along the walk, you check whether the coordinates are within the grid (0 <= r < rows and 0 <= c < cols). If they are, you add [r, c] to your result. You stop as soon as you have collected all rows * cols cells.

Step 1: Start at (rStart, cStart)

0start position

Collect the starting cell. Set side length = 1.

Step 2: Walk East 1, then South 1

012E(1)S(1)

Two moves, each of length 1. Collect any cell that falls in bounds.

Step 3: Walk West 2, then North 2

6501432W(2)N(2)

Side length increases to 2. Each pair of directions shares the same length.

Step 4: Walk East 3, then South 3

67895011043211xE(3)S(3)

Side length is now 3. The spiral keeps growing outward by one each pair.

Step 5: The pattern

E(1)S(1)W(2)N(2)E(3)S(3)...Side length grows by 1 after every two directions

Direction sequence: E, S, W, N. Side lengths: 1,1, 2,2, 3,3, 4,4, ... Stop once all rows * cols cells are collected.

Clean solution

def spiralMatrixIII(rows: int, cols: int, rStart: int, cStart: int) -> list[list[int]]:
    result = [[rStart, cStart]]
    dr = [0, 1, 0, -1]
    dc = [1, 0, -1, 0]
    d = 0
    steps = 1

    while len(result) < rows * cols:
        for _ in range(2):
            for _ in range(steps):
                rStart += dr[d]
                cStart += dc[d]
                if 0 <= rStart < rows and 0 <= cStart < cols:
                    result.append([rStart, cStart])
            d = (d + 1) % 4
        steps += 1

    return result

Complexity analysis

ApproachTimeSpace
Spiral simulationO(max(rows, cols)^2)O(rows * cols)

Time: the spiral must extend far enough to cover the entire grid. In the worst case (starting from a corner), the spiral side length grows to roughly 2 * max(rows, cols). The total number of steps walked is proportional to the area of the spiral, which is O(max(rows, cols)^2). This can exceed rows * cols because many steps land outside the grid.

Space: the output array holds exactly rows * cols coordinate pairs. The only extra variables are the direction index, step counter, and coordinates, which is O(1) auxiliary space.

Edge cases to watch for

  • Starting in a corner: the spiral walks three-quarters of each ring outside the grid before finding in-bounds cells. The time cost is higher, but the logic is unchanged.
  • 1x1 grid: the starting cell is the only cell. The while loop condition fails immediately, and you return [[rStart, cStart]].
  • Single row or single column: the spiral still works. Most steps land out of bounds, but the boundary check filters them correctly.
  • Starting outside the center: asymmetric grids where the start is near an edge cause more out-of-bounds steps early on, but the algorithm handles it without special cases.
  • Large grids: the solution is efficient enough. The constant factor is small since each step is just an addition and a bounds check.

The building blocks

This problem combines two reusable patterns:

Direction cycling: the four-direction rotation (east, south, west, north) using direction arrays dr = [0, 1, 0, -1] and dc = [1, 0, -1, 0]. You cycle through them with d = (d + 1) % 4. This same technique appears in robot simulation problems, BFS on grids, and any problem that requires walking in cardinal directions.

Simulation with boundary filtering: rather than computing which cells to visit, you simulate the full walk and filter by a predicate. The predicate here is a simple bounds check, but the structure works for any validity condition. Walk, check, collect.

From understanding to recall

You have seen how the spiral walk works: four directions, paired side lengths that grow by one, and a bounds check at each step. The logic fits in about ten lines of code. But those ten lines have several details that are easy to get wrong under pressure: the direction arrays, the inner loop that runs twice per side-length increment, and the placement of the modular arithmetic on the direction index.

These are not conceptual gaps. They are mechanical details that slip from memory without reinforcement. Spaced repetition is the fix. You write the direction arrays, the nested loop structure, and the bounds check from scratch. You do it today, again in a few days, and again the following week. After a handful of reps, you see "spiral walk from a starting point" and the code appears on your screen without hesitation.

Related posts

CodeBricks breaks Spiral Matrix III into its direction-cycling and simulation building blocks, then drills them independently with spaced repetition. You type the direction arrays, the paired-length loop, and the boundary filter from memory until it is automatic. When a spiral or grid simulation problem appears in your interview, you do not think about it. You just write it.