螺旋矩阵
Spiral Matrix
按顺时针螺旋的顺序,从外到内把 m × n 矩阵里的所有元素依次取出来。
在力扣中练习题目描述
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]] 输出:[1,2,3,6,9,8,7,4,5]
示例 2:
输入: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]
提示:
m == matrix.lengthn == matrix[i].length1 <= m, n <= 10-100 <= matrix[i][j] <= 100
解题思路
四条边界 · 逐层收缩
上下左右各走一条边,走完就缩一格
按“向右、向下、向左、向上”一格格模拟需要记录方向和访问状态。也可以维护尚未读取区域的四条边界,每次读完一条边就把它从矩形里去掉。
自拟矩阵 [[5,8,2,9,4],[7,1,6,3,0],[12,11,10,14,13]] 是 3×5 的长方形,值不按访问顺序排列,因此要真正跟随位置读取。
四条边界围住尚未访问的矩形,按上、右、下、左收缩。
读上边后 top 加一,右边后 right 减一,底边后 bottom 减一,左边后 left 加一。后续边都要使用最新范围,四个角才不会重复。
外圈完成后内部只剩 [1,6,3] 一行,读完上边就已经读完它。底边必须重新判断 top<=bottom;同理左边也要检查 left<=right。最终每个位置只追加一次,而不是按值去重。
易错点
- 右边那一列从 0 开始遍历而不是从 top 开始,右上角那一格会被收两次。
- 底边、左边不加 if:只剩一行(或一列)时会把同一行倒着再收一遍。
- while 条件两个都要写:只留一个时,单行或单列矩阵会多走一轮,甚至把元素重复收集。
方向数组 · 撞墙右转
站在格子上往前走,走不动就右转 90°
正确的方向模拟,需要访问标记;主解法用四条边界省掉这部分辅助空间。
另一种写法更像「模拟走路」:站在 (r, c) 上,朝当前方向走一步;如果下一步越界,或者踩到已经访问过的格子,就右转 90° 再走。
用一个 seen 数组记录走过哪些格子,循环 m * n 次正好把所有格子走完。
好处是不用维护四条边,思路和题目描述一字不差;代价是多开一个 O(m·n) 的 visited 数组。方向数组按「右、下、左、上」排列,右转就是 (d + 1) % 4。
写的时候注意:判定「下一步不能走」之后,要用转向后的方向重新算一次 nr, nc,否则还停留在原来的方向上,会在原地打转。
- 1初始位置 (0,0),方向朝右,seen 全部为 false。
- 2收集当前格并标记 seen。
- 3算下一步:越界或已访问就 d = (d + 1) % 4,再重算下一步。
- 4移动过去,重复 m * n 次。
易错点
- 转向后忘记重算 nr、nc,下一步还是原来的方向,死循环。
- seen 标记写在 push 之后才做,容易在转角处把同一格重复加入结果。
代码与演示
主解法配有图解;对照解法用于比较思路、复杂度或实现方式。
matrix = [[5,8,2,9,4],[7,1,6,3,0],[12,11,10,14,13]]四条边界围住尚未访问的矩形,按上、右、下、左收缩。
查看 C++ 代码
延伸阅读
- 螺旋矩阵 II(59)是反过来的:按螺旋顺序往 n × n 矩阵里填 1 到 n²。
- 「逐层处理」的边界写法在旋转图像(48)里也用得上,只是那一题动手的是四条边上的元素交换。
- 之字形遍历(103、498)不要硬套四条边,方向数组 + 转向判断更省事。