-
Notifications
You must be signed in to change notification settings - Fork 0
/
spiral_matrix.py
156 lines (126 loc) · 4.41 KB
/
spiral_matrix.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/env python3
# Spiral Matrix
#
# https://leetcode.com/problems/spiral-matrix
#
# Given an m x n matrix, return all elements of the matrix in spiral order.
from typing import List
def test():
"""
Run `pytest <this-file>`.
"""
def test_algo(algo):
assert algo(matrix=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]) == [
1,
2,
3,
6,
9,
8,
7,
4,
5,
]
assert algo(matrix=[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) == [
1,
2,
3,
4,
8,
12,
11,
10,
9,
5,
6,
7,
]
assert algo(matrix=[[7], [9], [6]]) == [
7,
9,
6,
]
# Test all different algorithms/implementations
solution = Solution()
for algo in [solution.recursion, solution.transpose]:
test_algo(algo)
class Solution:
def recursion(self, matrix: List[List[int]]) -> List[int]:
"""
Approach: Recursive spiralling.
Idea: In each recursive call, follow the outside edge of the square, and the recurse into the rest inside.
Time: O(n): Given n elements in the matrix, we visit each element once taking O(1).
Space: O(1): No additional memory is used.
Leetcode: 33 ms runtime, 16.59 MB memory
"""
row_count = len(matrix)
col_count = len(matrix[0])
going_in_dir_diff = {
"north": (-1, 0),
"south": (1, 0),
"east": (0, 1),
"west": (0, -1),
}
def steps_in_dir(start_cell, dir: str, steps: int):
(row_diff, col_diff) = going_in_dir_diff[dir]
(row_idx, col_idx) = start_cell
for _ in range(0, steps):
row_idx += row_diff
col_idx += col_diff
yield (row_idx, col_idx)
def cell_value(cell):
(row_idx, col_idx) = cell
return matrix[row_idx][col_idx]
spiral_order = []
def spiral(start_cell, row_count: int, col_count: int):
if col_count == 0 or row_count == 0:
return
spiral_order.append(cell_value(start_cell))
latest_cell = start_cell
# Go east.
for cell in steps_in_dir(latest_cell, "east", steps=col_count - 1):
spiral_order.append(cell_value(cell))
latest_cell = cell
if row_count == 1:
return
# Go south.
for cell in steps_in_dir(latest_cell, "south", steps=row_count - 1):
spiral_order.append(cell_value(cell))
latest_cell = cell
if col_count == 1:
return
# Go west.
for cell in steps_in_dir(latest_cell, "west", steps=col_count - 1):
spiral_order.append(cell_value(cell))
latest_cell = cell
# Go north.
for cell in steps_in_dir(latest_cell, "north", steps=row_count - 2):
spiral_order.append(cell_value(cell))
latest_cell = cell
# Start inner spiral one step east.
spiral(
next(steps_in_dir(latest_cell, "east", steps=1)),
row_count - 2,
col_count - 2,
)
spiral((0, 0), row_count, col_count)
return spiral_order
def transpose(self, matrix: List[List[int]]) -> List[int]:
"""
Approach: Traverse and remove row, then rotate anti-clockwise.
Idea: See approach above.
Time: O(n): Given n elements in the matrix, we visit each element once taking O(1).
Space: O((n+m)(n*m)): Given an m x n matrix, we recurse n+m times, and each time we rotate the matrix anti-clockwise, taking O(n*m).
Leetcode: 43 ms runtime, 16.59 MB memory
"""
def rotate_anti_clockwise(matrix: List[List[int]]) -> List[List[int]]:
transposed = list(zip(*matrix))
return list(reversed(transposed))
def spiral(matrix: List[List[int]]) -> List[int]:
if matrix:
return list(matrix.pop(0)) + spiral(
rotate_anti_clockwise(matrix)
)
else:
return []
return spiral(matrix)