For a square we can define the spiral order in terms of concentric disks.

But for rectangles it can be hard.

Instead, we can find a robust, local definition:
- Start at (0, 0) and move rightwards.
- As long as we have not yet processed all matrix elements, we keep moving.
- If the next move would go outside matrix borders or would step into an already visited cell, turn ninety degrees clockwise and move on.
To be able to mark a visited cell, we can assign None.
Time: , space:
.
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
m, n = len(matrix), len(matrix[0])
dir_map = {
(0, 1): (1, 0), # right -> down
(1, 0): (0, -1), # down -> left
(0, -1): (-1, 0), # left -> up
(-1, 0): (0, 1), # up -> right
}
total = m * n
spiral = []
r, c = 0, 0
curr_dir = (0, 1) # right
while len(spiral) < total:
# Invariant: (r, c) is valid, new element
spiral.append(matrix[r][c])
matrix[r][c] = None
dr, dc = curr_dir
next_r, next_c = r + dr, c + dc
if (
(next_r < 0 or next_r > m - 1)
or (next_c < 0 or next_c > n - 1)
or matrix[next_r][next_c] is None
):
# Change direction
curr_dir = dir_map[curr_dir]
dr, dc = curr_dir
next_r, next_c = r + dr, c + dc
r, c = next_r, next_c
return spiral
If updating the matrix is not feasible, we can use the below two observations to skip updating:
- Each of the four borders of the matrix serves to bound movement in one specific direction. So, we can make boundary check specific to a direction.
- Second time we move rightwards, we can move one step less. That holds for other directions as well. So, on every change direction we can update the borders accordingly.
from enum import Enum
class Direction(Enum):
Right = (0, 1)
Down = (1, 0)
Left = (0, -1)
Up = (-1, 0)
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
m, n = len(matrix), len(matrix[0])
dir_map = {
Direction.Right: Direction.Down,
Direction.Down: Direction.Left,
Direction.Left: Direction.Up,
Direction.Up: Direction.Right,
}
total = m * n
row_min, row_max = 0, m - 1
col_min, col_max = 0, n - 1
spiral = []
r, c = 0, 0
curr_dir = Direction.Right
while len(spiral) < total:
# Invariant: (r, c) is valid, new element
spiral.append(matrix[r][c])
dr, dc = curr_dir.value
next_r, next_c = r + dr, c + dc
if (
(curr_dir == Direction.Up and next_r < row_min)
or (curr_dir == Direction.Down and next_r > row_max)
or (curr_dir == Direction.Left and next_c < col_min)
or (curr_dir == Direction.Right and next_c > col_max)
):
# Change direction
next_dir = dir_map[curr_dir]
if curr_dir == Direction.Right:
col_max -= 1
elif curr_dir == Direction.Down:
row_max -= 1
elif curr_dir == Direction.Left:
row_min += 1
else:
col_min += 1
curr_dir = next_dir
dr, dc = curr_dir.value
next_r, next_c = r + dr, c + dc
r, c = next_r, next_c
return spiral
Leave a comment