LeetCode 885 - Spiral Matrix III
The problem is asking us to generate the coordinates of cells in a 2D grid that we would visit if we started at a given cell (rStart, cStart) and walked in a clockwise spiral pattern.
Difficulty: 🟡 Medium
Topics: Array, Matrix, Simulation
Solution
Problem Understanding
The problem is asking us to generate the coordinates of cells in a 2D grid that we would visit if we started at a given cell (rStart, cStart) and walked in a clockwise spiral pattern. The grid has dimensions rows x cols, and the top-left corner is (0, 0) while the bottom-right corner is (rows - 1, cols - 1). We start by facing east, move one step at a time, and follow the spiral pattern: east, south, west, north, then repeat with expanding steps.
The input parameters are rows and cols specifying the grid size, and rStart and cStart specifying the starting position. The output should be a list of all positions visited in the exact order we encounter them, including handling steps outside the grid boundaries (these should be skipped in the output).
Constraints tell us that the grid is relatively small (1 <= rows, cols <= 100), so brute-force approaches are feasible but unnecessary. Edge cases include starting at the grid boundary, having only a single row or column, and grids of size 1x1 where the spiral has minimal movement. The problem guarantees a valid starting position within the grid.
Approaches
The brute-force approach involves simulating every single step of the spiral in an unbounded coordinate plane and checking at each step whether the current cell is inside the grid. We would continue this until all cells in the grid have been visited. This works because the spiral eventually reaches all cells. However, it is slightly inefficient because it calculates many positions outside the grid that do not contribute to the result.
The optimal approach leverages the fact that the spiral moves in predictable steps that increase after every two directions. By using a direction vector and controlling the step length incrementally, we can systematically generate only the coordinates we need. The key insight is that the spiral's movement pattern is repetitive and structured, so we can iterate in a controlled loop until we have visited all rows * cols cells. This avoids unnecessary checks or additional memory overhead for visited cells since each grid cell is guaranteed to be visited exactly once.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(max(rows, cols)^2) | O(rows * cols) | Simulate every spiral step, check grid boundaries |
| Optimal | O(rows * cols) | O(rows * cols) | Systematic spiral generation with controlled step increments |
Algorithm Walkthrough
- Initialize a result list to store the coordinates of cells visited within the grid. Add the starting position
(rStart, cStart)to this list. - Define the four possible movement directions as a sequence: east
(0, 1), south(1, 0), west(0, -1), and north(-1, 0). - Set up variables to track the current position
(r, c)and the current step length. The step length starts at 1 and increases by 1 after completing two directions (i.e., a horizontal and a vertical move). - Loop until the result list contains
rows * colscoordinates. For each direction in the sequence, movestep_lengthtimes in that direction, updating the current coordinates at each move. - After each move, check if the current position is inside the grid boundaries. If it is, append it to the result list.
- After completing movement in two directions (east and south, or west and north), increment the step length by 1 to expand the spiral.
- Continue this process until all cells are recorded in the result list.
Why it works: The algorithm works because it mimics the exact spiral pattern required by the problem and ensures every cell in the grid is visited once. The step length increment every two directions guarantees that the spiral expands outward correctly, and checking boundaries ensures only valid grid positions are included in the result.
Python Solution
from typing import List
class Solution:
def spiralMatrixIII(self, rows: int, cols: int, rStart: int, cStart: int) -> List[List[int]]:
result = [[rStart, cStart]]
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] # east, south, west, north
steps = 1
r, c = rStart, cStart
while len(result) < rows * cols:
for i in range(4):
dr, dc = directions[i]
for _ in range(steps):
r += dr
c += dc
if 0 <= r < rows and 0 <= c < cols:
result.append([r, c])
if i % 2 == 1:
steps += 1 # increment step length after every vertical move
return result
The Python solution follows the algorithm exactly. The directions array ensures consistent movement in the spiral pattern. The steps variable controls the expansion of the spiral. The inner loop moves the current position and checks if the coordinates are within the grid before adding to result. Step length is incremented after every two moves (vertical direction) to maintain the spiral expansion.
Go Solution
func spiralMatrixIII(rows int, cols int, rStart int, cStart int) [][]int {
result := [][]int{{rStart, cStart}}
directions := [][]int{{0,1},{1,0},{0,-1},{-1,0}} // east, south, west, north
steps := 1
r, c := rStart, cStart
for len(result) < rows*cols {
for i := 0; i < 4; i++ {
dr, dc := directions[i][0], directions[i][1]
for j := 0; j < steps; j++ {
r += dr
c += dc
if r >= 0 && r < rows && c >= 0 && c < cols {
result = append(result, []int{r, c})
}
}
if i % 2 == 1 {
steps++
}
}
}
return result
}
In Go, slices are used for dynamic array construction, and the approach mirrors Python. Go requires explicit indexing for 2D slices, and bounds checking is done with >= and <. Slice appends are efficient for building the result.
Worked Examples
Example 1
Input: rows = 1, cols = 4, rStart = 0, cStart = 0
Step-by-step:
| Step | Direction | Position | Added to Result |
|---|---|---|---|
| 0 | Start | (0,0) | Yes |
| 1 | East | (0,1) | Yes |
| 2 | East | (0,2) | Yes |
| 3 | East | (0,3) | Yes |
Output: [[0,0],[0,1],[0,2],[0,3]]
Example 2
Input: rows = 5, cols = 6, rStart = 1, cStart = 4
Step-by-step (partial for clarity):
| Step | Direction | Position | Added to Result |
|---|---|---|---|
| 0 | Start | (1,4) | Yes |
| 1 | East | (1,5) | Yes |
| 2 | South | (2,5) | Yes |
| 3 | West | (2,4) | Yes |
| 4 | West | (2,3) | Yes |
| 5 | North | (1,3) | Yes |
Continues until all 30 cells are visited in spiral order.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(rows * cols) | Each cell in the grid is visited exactly once |
| Space | O(rows * cols) | Storing the coordinates of all cells in the result list |
Since each position is added once and the spiral expands predictably, the algorithm is linear in the number of grid cells.
Test Cases
# Provided examples
assert Solution().spiralMatrixIII(1, 4, 0, 0) == [[0,0],[0,1],[0,2],[0,3]] # single row
assert Solution().spiralMatrixIII(5, 6, 1, 4) == [
[1,4],[1,5],[2,5],[2,4],[2,3],[1,3],[0,3],[0,4],[0,5],
[3,5],[3,4],[3,3],[3,2],[2,2],[1,2],[0,2],[4,5],[4,4],
[4,3],[4,2],[4,1],[3,1],[2,1],[1,1],[0,1],[4,0],[3,0],
[2,0],[1,0],[0,0]
]
# Edge cases
assert Solution().spiralMatrixIII(1, 1, 0, 0) == [[0,0]] # single cell
assert Solution().spiralMatrixIII(3, 3, 1, 1) == [
[1,1],[1,2],[2,2],[2,1],[2,0],[1,0],[0,0],[