题目重点:从 visited+方向转到 top/bottom/left/right 四边界,处理单行单列、非方阵和逆时针变体。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 主问题为矩形二维数组,返回所有元素一次;空矩阵或 0 列返回空列表。
- 训练版默认从左上角出发先向右(顺时针);逆时针从左上角先向下,题目未说明起点,需现场核对。
- 行宽不一致的 ragged 输入报错;填充矩阵是另外的输出方向变体,本文代码只实现读遍历。
解题路线
- 维护未访问矩形的 top/bottom/left/right,初始覆盖整个矩阵。顺时针每轮读上边向右、右边向下、下边向左、左边向上,然后收缩对应边界。
- 每读完一条边都检查剩余边界是否仍有效;否则单行或单列时会重复读同一边。
- 逆时针对应先读左边向下、下边向右、右边向上、上边向左,依序收缩。
- 不需要 visited 布尔矩阵,因为边界恰好刻画尚未读过的矩形;填充题可复用相同坐标顺序但把读取替换为顺序写入。
手动走一遍
3×3 的 1..9 网格,顺时针为 1,2,3,6,9,8,7,4,5;逆时针从左上角先向下则为 1,4,7,8,9,6,3,2,5。对 1×3 行只读一次上边,对 3×1 列只读一次左边。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
from collections.abc import Sequence
def spiral_order(matrix: Sequence[Sequence[int]], clockwise: bool = True) -> list[int]:
if not matrix:
return []
cols = len(matrix[0])
if any(len(row) != cols for row in matrix):
raise ValueError("matrix must be rectangular")
if cols == 0:
return []
top, bottom = 0, len(matrix) - 1
left, right = 0, cols - 1
output = []
while top <= bottom and left <= right:
if clockwise:
output.extend(matrix[top][left:right + 1]); top += 1
for r in range(top, bottom + 1): output.append(matrix[r][right])
right -= 1
if top <= bottom:
output.extend(reversed(matrix[bottom][left:right + 1])); bottom -= 1
if left <= right:
for r in range(bottom, top - 1, -1): output.append(matrix[r][left])
left += 1
else:
for r in range(top, bottom + 1): output.append(matrix[r][left])
left += 1
if left <= right:
output.extend(matrix[bottom][left:right + 1]); bottom -= 1
if top <= bottom and left <= right:
for r in range(bottom, top - 1, -1): output.append(matrix[r][right])
right -= 1
if top <= bottom and left <= right:
output.extend(reversed(matrix[top][left:right + 1])); top += 1
return output
if __name__ == "__main__":
grid = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
assert spiral_order(grid) == [1, 2, 3, 6, 9, 8, 7, 4, 5]
assert spiral_order(grid, False) == [1, 4, 7, 8, 9, 6, 3, 2, 5]
复杂度
每个单元最多访问一次,O(RC) 时间;除返回的 RC 个元素外 O(1) 工作空间。visited+direction 版本同为 O(RC) 时间,但额外需要 O(RC) 访问标记。
易错点与追问
容易失分的边界
- 收缩上边后未检查 top≤bottom,单行被重复读。
- 逆时针单列读完左边后仍去读右边,出现重复。
- 循环终止条件只检查行边界,忽略列边界。
- 声称本文实现了题目提到的填充变体;代码只给遍历,填充需另写坐标写入。
继续深挖
- 给定 R、C 将 1..RC 按螺旋顺序填入矩阵,如何复用边界代码?
- 如果要求从右下角开始或向外螺旋,起点与方向怎样参数化?
- 如果只能惰性迭代而不分配结果列表,如何实现生成器?
