54. 螺旋矩阵
✨核心逻辑
本题采用 模拟边界收缩(按层遍历) 的策略:
- 定义边界:维护四个变量
top(上边界)、bottom(下边界)、left(左边界)、right(右边界),分别代表当前未遍历矩阵区域的上下左右边缘。 - 顺时针遍历:在
top <= bottom且left <= right的前提下,逐步按顺时针方向收缩边界:- 从左到右遍历上边界,遍历完后
top++(上边界向下收缩一行)。 - 从上到下遍历右边界,遍历完后
right--(右边界向左收缩一列)。 - 如果此时
top <= bottom,则从右到左遍历下边界,遍历完后bottom--(下边界向上收缩一行)。 - 如果此时
left <= right,则从下到上遍历左边界,遍历完后left++(左边界向右收缩一列)。
- 从左到右遍历上边界,遍历完后
- 循环终止:当四个边界交错(
top > bottom或left > right)时,说明所有元素都已遍历完毕,循环结束。
🔥代码实现(含详细变量注释)
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
// res:用于记录最终按螺旋顺序输出的元素列表
List<Integer> res = new ArrayList<>();
// 边界情况:如果矩阵为空,直接返回空列表
if (matrix.length == 0) {
return res;
}
// top:初始化当前未遍历区域的上边界索引
int top = 0;
// bottom:初始化当前未遍历区域的下边界索引(矩阵总行数 - 1)
int bottom = matrix.length - 1;
// left:初始化当前未遍历区域的左边界索引
int left = 0;
// right:初始化当前未遍历区域的右边界索引(矩阵总列数 - 1)
int right = matrix[0].length - 1;
// 当上边界 <= 下边界,且左边界 <= 右边界时,说明还有未遍历的矩形区域,继续循环
while (top <= bottom && left <= right) {
// 第一步:从左到右遍历上边界
for (int i = left; i <= right; i++) {
res.add(matrix[top][i]);
}
// 上边界完成遍历,向下收缩一行
top++;
// 第二步:从上到下遍历右边界
for (int i = top; i <= bottom; i++) {
res.add(matrix[i][right]);
}
// 右边界完成遍历,向左收缩一列
right--;
// 第三步:如果此时上边界依然不大于下边界,说明还有底部行未遍历
if (top <= bottom) {
// 从右到左遍历下边界
for (int i = right; i >= left; i--) {
res.add(matrix[bottom][i]);
}
// 下边界完成遍历,向上收缩一行
bottom--;
}
// 第四步:如果此时左边界依然不大于右边界,说明还有左侧列未遍历
if (left <= right) {
// 从下到上遍历左边界
for (int i = bottom; i >= top; i--) {
res.add(matrix[i][left]);
}
// 左边界完成遍历,向右收缩一列
left++;
}
}
// 返回最终按顺时针螺旋顺序收集的列表
return res;
}
}
- ⏱️复杂度分析
时间复杂度:O(m * n),其中 m 和 n 分别是矩阵的行数和列数。我们需要将矩阵中的每一个元素访问且仅访问一次,因此总操作次数与矩阵元素总数成正比。
空间复杂度:O(1)(不考虑输出数组占用的空间)。我们只使用了 top、bottom、left、right 以及循环变量 i 等常数个额外的变量来进行边界控制
🔍总结一下:
一定要注意当走完左右方向以及上下方向的时候,后面两个循环要加if条件判断,否则会重复加入之前的路径元素
eg:
