54. Spiral Matrix
Quick Identifier
- Topic: arrays-hashing
- Pattern: Matrix Simulation (Shrinking Boundaries)
- Hint: Set up four invisible walls:
top,bottom,left,right. Walk along a wall, then shrink it inward. Repeat until the walls crush you. - Difficulty: Medium
- Companies: Amazon, Google, Meta, Microsoft, Apple
- LeetCode: 54
Quick Sights
- Time Complexity:
O(m * n). We visit every single cell in the $M \times N$ matrix exactly once. - Space Complexity:
O(1). Excluding the space required for the output array, we only use four integer pointers to represent the boundaries. Novisitedmatrix is needed. - Core Mechanism: Maintain four boundaries:
top,bottom,left,right.- Traverse from
lefttorightalong thetoprow. Then incrementtop(move the top wall down). - Traverse from
toptobottomalong therightcolumn. Then decrementright(move the right wall left). - Traverse from
righttoleftalong thebottomrow. Then decrementbottom(move the bottom wall up). - Traverse from
bottomtotopalong theleftcolumn. Then incrementleft(move the left wall right). - Repeat while
left <= rightandtop <= bottom.
- Traverse from
Concept Explanation
As a senior engineer, don't overcomplicate this with Direction Vectors (dx, dy) and visited matrices. That is an $O(m \cdot n)$ space approach designed for Graph Traversals (DFS/BFS). This is not a graph problem; it's a rigid geometry problem.
The simplest way to conceptualize this is the Crushing Room trap from Indiana Jones.
- You are in a room with four walls.
- You walk flush against the Top wall from the Left corner to the Right corner.
- Once you finish, the Top wall violently shifts downward one step. You can never walk there again.
- Now you walk flush against the Right wall, from Top to Bottom.
- The Right wall shifts inward.
- You repeat this clockwise pattern. Because the walls actually physically shrink the dimensions of the room, you never have to "remember" where you've been (no
visitedmatrix). The walls prevent you from going back. - The loop terminates when the walls crash into each other (
top > bottomorleft > right).
Diagram
This flowchart highlights the clockwise traversal and the critical boundary checks needed for non-square matrices.
Loading diagram…
Study Pattern (SDE3+)
- 0–3 min: Immediately describe the "Four Boundaries" shrinking strategy. Clearly contrast it against the naive "Direction Array + Visited Matrix" approach, pointing out that yours saves $O(m \cdot n)$ space.
- Implementation pass: The most common mistake in this problem happens with non-square rectangles (e.g., $1 \times 3$ or $3 \times 1$). After you sweep Top and Right, the walls might have crossed. You must add an
if (top <= bottom && left <= right)check before attempting to sweep the Bottom and Left rows to prevent duplicate counting. - 5 min extension: Discuss how to reverse the algorithm. What if you were given an empty matrix and the 1D array of numbers, and asked to fill the matrix in a spiral pattern? (LeetCode 59: Spiral Matrix II). The code structure is 100% identical, you just swap
result.push(matrix[r][c])withmatrix[r][c] = currentNum++.
Approaches
- Simulation with Visited Map — Use
dx, dyarrays[0, 1, 0, -1]. If you hit an edge or avisitedcell, turn 90 degrees. Time: $O(m \cdot n)$, Space: $O(m \cdot n)$ extra space for visited map. Suboptimal. - Layer-by-Layer Shrinking Boundaries — Track four integer walls. Time: $O(m \cdot n)$, Space: $O(1)$ extra space. (Always pick this)
Optimal Solution
Go
func spiralOrder(matrix [][]int) []int {
if len(matrix) == 0 {
return nil
}
result := make([]int, 0, len(matrix)*len(matrix[0]))
top := 0
bottom := len(matrix) - 1
left := 0
right := len(matrix[0]) - 1
for top <= bottom && left <= right {
// 1. Sweep Top Row (Left to Right)
for c := left; c <= right; c++ {
result = append(result, matrix[top][c])
}
top++ // Shrink top wall downward
// 2. Sweep Right Column (Top to Bottom)
for r := top; r <= bottom; r++ {
result = append(result, matrix[r][right])
}
right-- // Shrink right wall leftward
// CRITICAL: Check if walls crossed before sweeping back.
// This prevents duplicates in non-square matrices (e.g., 1x3 or 3x1)
if top <= bottom && left <= right {
// 3. Sweep Bottom Row (Right to Left)
for c := right; c >= left; c-- {
result = append(result, matrix[bottom][c])
}
bottom-- // Shrink bottom wall upward
// 4. Sweep Left Column (Bottom to Top)
for r := bottom; r >= top; r-- {
result = append(result, matrix[r][left])
}
left++ // Shrink left wall rightward
}
}
return result
}JavaScript
function spiralOrder(matrix) {
if (matrix.length === 0) return [];
const result = [];
let top = 0;
let bottom = matrix.length - 1;
let left = 0;
let right = matrix[0].length - 1;
while (top <= bottom && left <= right) {
// 1. Top Row
for (let c = left; c <= right; c++) {
result.push(matrix[top][c]);
}
top++;
// 2. Right Col
for (let r = top; r <= bottom; r++) {
result.push(matrix[r][right]);
}
right--;
// Check condition before backwards sweeps
if (top <= bottom && left <= right) {
// 3. Bottom Row
for (let c = right; c >= left; c--) {
result.push(matrix[bottom][c]);
}
bottom--;
// 4. Left Col
for (let r = bottom; r >= top; r--) {
result.push(matrix[r][left]);
}
left++;
}
}
return result;
}Walkthrough
Input: matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
(3x3 square)
| Phase | Swept Elements | Pointer Updates | Current result |
|---|---|---|---|
| Initial | - | t=0, b=2, l=0, r=2 | [] |
| Sweep Top | [1, 2, 3] | t++ (t=1) | [1, 2, 3] |
| Sweep Right | [6, 9] | r-- (r=1) | [1, 2, 3, 6, 9] |
t<=b && l<=r? | 1<=2 && 0<=1 (Yes) | - | - |
| Sweep Bottom | [8, 7] | b-- (b=1) | [1, ..., 9, 8, 7] |
| Sweep Left | [4] | l++ (l=1) | [1, ..., 7, 4] |
| Loop Restart | - | t=1, b=1, l=1, r=1 | - |
| Sweep Top | [5] | t++ (t=2) | [1, ..., 4, 5] |
| Sweep Right | None | r-- (r=0) | [1, ..., 5] |
t<=b && l<=r? | 2<=1 (No) | Break! | - |
Output: [1, 2, 3, 6, 9, 8, 7, 4, 5]
Edge Cases
- A single row matrix
[[1, 2, 3]]. The Top sweep grabs everything. The Right sweep grabs nothing. The check fails, preventing the Bottom sweep from grabbing[2, 1]redundantly. - A single column matrix
[[1], [2], [3]]. The Top sweep grabs[1]. The Right sweep grabs[2, 3]. The check fails, preventing Left/Bottom sweeps.
Pitfalls
- Forgetting the
if (top <= bottom && left <= right)check in the middle of thewhileloop. If you have a $3 \times 5$ matrix, the loops will execute perfectly for the outer rings. But the innermost ring will just be a 1D line. Without the middle check, you will sweep down the line, and then mistakenly sweep back up the exact same line.
Similar Problems
- 59. Spiral Matrix II — Generating the matrix instead of reading it. Same loop structure.
- 048. Rotate Image — Matrix transformation.
Mind-Map Tags
#matrix #simulation #boundaries #spiral
Mark this page when you finish learning it.
Last updated on
Spotted something unclear or wrong on this page?