大家好,我是你们的算法小伙伴。今天我们来练习一道矩阵模拟 的经典中等题 ------LeetCode 54. 螺旋矩阵。这道题考察对边界控制 和循环逻辑的理解,是面试中考察细心程度和代码规范的高频题,非常适合用来锻炼逻辑思维。
题目描述
给你一个 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.length(行数)n == matrix[i].length(列数)1 <= m, n <= 10-100 <= matrix[i][j] <= 100
解题思路
核心思路:按层模拟,边界收缩
螺旋遍历的本质是按顺序绕圈:
- 从左到右(顶行)
- 从上到下(右列)
- 从右到左(底行)
- 从下到上(左列)
走完一圈后,收缩边界(上边界 + 1,下边界 - 1,左边界 + 1,右边界 - 1),继续遍历内层,直到所有元素都被访问。
最优解法:四边界 + 循环判定
核心变量(四个边界):
top:上边界(行号,初始 0)bottom:下边界(行号,初始matrix.length-1)left:左边界(列号,初始 0)right:右边界(列号,初始matrix[0].length-1)
逻辑步骤:
- 遍历顶行:从
left到right,遍历完top++ - 遍历右列:从
top到bottom,遍历完right-- - 遍历底行:从
right到left,遍历完bottom-- - 遍历左列:从
bottom到top,遍历完left++ - 循环终止 :当
top > bottom或left > right时,停止遍历。
代码实现
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> res = new ArrayList<>();
if (matrix == null || matrix.length == 0) {
return res;
}
// 1. 定义四个边界
int top = 0;
int bottom = matrix.length - 1;
int left = 0;
int right = matrix[0].length - 1;
// 2. 循环遍历,直到边界相遇
while (top <= bottom && left <= right) {
// ① 从左到右:遍历顶行
for (int i = left; i <= right; i++) {
res.add(matrix[top][i]);
}
top++; // 上边界下移
// ② 从上到下:遍历右列
// 需判断是否还有行可遍历(防止只有一行的情况)
if (top > bottom) break;
for (int i = top; i <= bottom; i++) {
res.add(matrix[i][right]);
}
right--; // 右边界左移
// ③ 从右到左:遍历底行
// 需判断是否还有列可遍历(防止只有一列的情况)
if (left > right) break;
for (int i = right; i >= left; i--) {
res.add(matrix[bottom][i]);
}
bottom--; // 下边界上移
// ④ 从下到上:遍历左列
if (top > bottom) break;
for (int i = bottom; i >= top; i--) {
res.add(matrix[i][left]);
}
left++; // 左边界右移
}
return res;
}
}
代码详解
1. 边界初始化
top = 0:初始指向第一行。bottom = matrix.length - 1:初始指向最后一行。left = 0:初始指向第一列。right = matrix[0].length - 1:初始指向最后一列。
2. 主循环
while (top <= bottom && left <= right):保证每次循环都是一个有效的矩形区域。
3. 四个方向遍历(重点!)
① 从左到右(顶行)
- 遍历范围:
[top][left]→[top][right] - 遍历结束后:
top++(这一行已经走完,下移一层)
② 从上到下(右列)
- 必须先判断
top > bottom:- 比如矩阵是
[1,2,3](一行),遍历完顶行后top=1,此时top > bottom,直接退出,避免重复添加。
- 比如矩阵是
- 遍历范围:
[top][right]→[bottom][right] - 遍历结束后:
right--(这一列已经走完,左移一层)
③ 从右到左(底行)
- 必须先判断
left > right:- 比如矩阵是
[[1],[2],[3]](一列),上两步走完后right=0,此时left > right,直接退出。
- 比如矩阵是
- 遍历范围:
[bottom][right]→[bottom][left] - 遍历结束后:
bottom--(这一行已经走完,上移一层)
④ 从下到上(左列)
- 必须先判断
top > bottom:防止行越界。 - 遍历范围:
[bottom][left]→[top][left] - 遍历结束后:
left++(这一列已经走完,右移一层)
4. 防越界判断(面试加分项!)
在遍历右列、底行、左列之前,都要再次判断边界是否交叉。
- 例如:如果矩阵只有一行(
m=1),遍历完顶行 后,top++会导致top > bottom,后续的循环直接跳过,不会出错。
示例模拟
以 示例 1 [[1,2,3],[4,5,6],[7,8,9]] 为例:
- 初始状态 :
top=0, bottom=2, left=0, right=2- 顶行(0 行):添加
1,2,3→top=1 - 右列(2 列):添加
6,9→right=1 - 底行(2 行):添加
8,7→bottom=1 - 左列(0 列):添加
4→left=1
- 顶行(0 行):添加
- 当前状态 :
top=1, bottom=1, left=1, right=1(中心只剩一个5)- 进入循环,
top<=bottom且left<=right成立。 - 顶行(1 行):添加
5→top=2 - 此时判断
top > bottom(2>1),break,循环结束。
- 进入循环,
- 最终结果 :
[1,2,3,6,9,8,7,4,5],完美匹配!
复杂度分析
| 指标 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(mn) | 每个元素只遍历一次,遍历次数等于矩阵元素总数 |
| 空间复杂度 | O(1) | 除了结果列表,只使用了常数个变量(边界) |
高频易错点总结
- 忘记判断边界 :遍历右列、底行、左列前,必须加
if判断,否则会出现元素重复 或数组越界错误。 - 方向顺序错误 :必须严格遵循左→右 → 上→下 → 右→左 → 下→上的顺序。
- 边界更新时机 :先遍历,后更新边界(
top++等操作必须在遍历完那一圈之后做)。 - 矩阵形状 :
- 处理 1xN (一行)或 Nx1 (一列)的矩阵时,代码能自动处理,因为边界判断会触发
break。
- 处理 1xN (一行)或 Nx1 (一列)的矩阵时,代码能自动处理,因为边界判断会触发
总结
这道题的核心是 **「边界收缩」**。
- 不要死记硬背,只要记住:走一圈,四个方向各走一遍,然后把包围圈往里缩一点,继续走。
- 这道题的代码模板非常通用,面试中如果遇到螺旋矩阵 II(LeetCode 59),只需要稍微修改初始化逻辑即可套用。
今天的每日算法练习就到这里,我们明天再见!👋