LeetCode 54. 螺旋矩阵
题目描述
✅ 54. 螺旋矩阵


题意分析
输入一个
m行n列的矩阵,要求按顺时针螺旋的顺序——从左上角出发向右,沿最外圈绕行一周,再进入内圈继续绕——把所有元素依次放进一个一维列表返回。约束给得非常小,
m、n都不超过 10。这个信号很明确:本题不考效率,随便怎么写都跑得完,考的是能不能把「什么时候转弯、什么时候停」处理得一个不多、一个不少——输出长度必须恰好是 $m \cdot n$,重复和遗漏都算错。矩阵不保证是方阵,
m和n可以相差悬殊,这是全题最重要的一条题设。方阵一圈一圈剥下来,每圈四条边都完整存在;而非方阵剥到最后可能只剩一行或一列,「圈」退化成一条线段,只有去程没有回程。绝大多数错误答案都死在这个形态上。约束同时保证
m >= 1、n >= 1,矩阵不会为空,不需要判空入口。单行、单列和1 x 1都是合法输入,其中1 x 1是最小的退化用例,单行、单列则是上面说的「圈退化成线段」在整个矩阵尺度上的体现。
解法:四边界收缩
核心思路
用
top、bottom、left、right表示尚未遍历的矩形边界。每轮依次遍历上边、右边、下边和左边,并在遍历完一条边后向内收缩对应边界;遍历下边和左边前要确认区域仍然存在,避免单行或单列重复。
解题步骤
- 初始化四个边界,分别指向矩阵最外层。
- 从左到右遍历上边并收缩
top,再从上到下遍历右边并收缩right。- 若仍有行,从右到左遍历下边并收缩
bottom。- 若仍有列,从下到上遍历左边并收缩
left,重复直到边界交错。
代码实现
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
int top = 0;
int bottom = matrix.length - 1;
int left = 0;
int right = matrix[0].length - 1;
List<Integer> result = new ArrayList<>(matrix.length * matrix[0].length);
while (top <= bottom && left <= right) {
for (int col = left; col <= right; col++) {
result.add(matrix[top][col]);
}
top++;
for (int row = top; row <= bottom; row++) {
result.add(matrix[row][right]);
}
right--;
if (top <= bottom) {
for (int col = right; col >= left; col--) {
result.add(matrix[bottom][col]);
}
bottom--;
}
if (left <= right) {
for (int row = bottom; row >= top; row--) {
result.add(matrix[row][left]);
}
left++;
}
}
return result;
}
}
func spiralOrder(matrix [][]int) []int {
top, bottom := 0, len(matrix)-1
left, right := 0, len(matrix[0])-1
result := make([]int, 0, len(matrix)*len(matrix[0]))
for top <= bottom && left <= right {
for col := left; col <= right; col++ {
result = append(result, matrix[top][col])
}
top++
for row := top; row <= bottom; row++ {
result = append(result, matrix[row][right])
}
right--
if top <= bottom {
for col := right; col >= left; col-- {
result = append(result, matrix[bottom][col])
}
bottom--
}
if left <= right {
for row := bottom; row >= top; row-- {
result = append(result, matrix[row][left])
}
left++
}
}
return result
}
复杂度分析
- 时间复杂度:$O(mn)$,每个元素恰好访问一次。
- 空间复杂度:$O(1)$,不计返回结果。
关键点总结
- 四个边界描述的是尚未遍历的区域,每走完一条边就立即收缩。
- 遍历下边前检查
top <= bottom,遍历左边前检查left <= right。- 循环条件必须同时满足行区间和列区间有效。
易错点总结
- 省略下边或左边遍历前的边界检查,会在单行、单列矩阵中重复元素。
- 输出一条边后忘记收缩对应边界,会重复访问外圈。
- 右边应从更新后的
top开始,下边应从更新后的right开始,避免重复拐角。- 外层循环使用
||会在某一维已经越界后继续访问矩阵。
相似题目
| 题目 | 难度 | 考察点 |
|---|---|---|
| 59. 螺旋矩阵 II | 中等 | 反向操作:按同一条螺旋路径向空方阵里填数,因保证方阵,无需单行单列去重检查 |
| 剑指 Offer 29. 顺时针打印矩阵 | 简单 | 与本题同题换皮,但输入允许空矩阵,动手前要多一步判空 |
| 48. 旋转图像 | 中等 | 同样按圈分层处理矩阵,操作换成四点原地轮换,考坐标映射而非遍历路径 |