文章目录
前言
本专栏文章为《LeetCode 热题 100》的刷题题解,相关内容如有侵权,立即删除。
一、题目
1、原题链接
2、题目描述
二、个人思路整理
1、思路分析
核心思路:按层模拟(边界收缩法)
- 维护四个边界指针:
top = 0(上边界)bottom = m - 1(下边界)left = 0(左边界)right = n - 1(右边界)
- 顺时针遍历分为四步,每走完一条边就将对应边界向内收缩 1 格,并检查是否越界:
- 从左向右:遍历
matrix[top][left ... right]→ \rightarrow →top++,若top > bottom则结束 - 从上向下:遍历
matrix[top ... bottom][right]→ \rightarrow →right--,若left > right则结束。 - 从右向左:遍历
matrix[bottom][right ... left]→ \rightarrow →bottom--,若top > bottom则结束。 - 从下向上:遍历
matrix[bottom ... top][left]→ \rightarrow →left++,若left > right则结束。
- 从左向右:遍历
2、解题代码
cpp
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
// 空矩阵直接返回空数组
// 既能覆盖 0 × 0([])的情况,也能覆盖 m × 0([[]])的边界情况
if (matrix.empty() || matrix[0].empty()) {
return {};
}
// 初始化四个边界指针,界定当前未遍历的子矩阵区域
int top = 0, bottom = matrix.size() - 1;
int left = 0, right = matrix[0].size() - 1;
vector<int> ans;
while (true) {
// 1. 从左到右遍历当前的上边界行
for (int j = left; j <= right; j++) {
ans.push_back(matrix[top][j]);
}
// 上边界遍历完毕,向下收缩;若上边界超越下边界,说明所有元素已遍历完成
if (++top > bottom) {
break;
}
// 2. 从上到下遍历当前的右边界列
for (int i = top; i <= bottom; i++) {
ans.push_back(matrix[i][right]);
}
// 右边界遍历完毕,向左收缩;若右边界越过左边界,结束遍历
if (--right < left) {
break;
}
// 3. 从右到左遍历当前的下边界行
for (int j = right; j >= left; j--) {
ans.push_back(matrix[bottom][j]);
}
// 下边界遍历完毕,向上收缩;若下边界超越上边界,结束遍历
if (--bottom < top) {
break;
}
// 4. 从下到上遍历当前的左边界列
for (int i = bottom; i >= top; i--) {
ans.push_back(matrix[i][left]);
}
// 左边界遍历完毕,向右收缩;若左边界越过有边界,结束遍历
if (++left > right) {
break;
}
}
return ans;
}
};
复杂度分析
- 时间复杂度: O ( m × n ) O(m \times n) O(m×n),矩阵中每个元素恰好访问一次。
- 空间复杂度: O ( 1 ) O(1) O(1),仅需常数级别的边界变量(返回值不计入额外空间)。
三、知识风暴
边界收缩法(按层模拟) 是「螺旋矩阵」这类矩阵遍历题的核心技巧:通过维护上下左右四个边界指针,逐层向内收缩,从而按顺时针顺序访问矩阵的每一个元素。由于题目要求 O ( 1 ) O(1) O(1) 额外空间,这类题通常采用「边界指针 + 循环收缩」的方式,避免使用额外的访问标记数组。
算法核心思想:
- 维护四个边界指针 :用
top、bottom、left、right界定当前未遍历的子矩阵区域,每遍历完一条边就向内收缩一格。 - 顺时针四步遍历:依次完成「从左到右、从上到下、从右到左、从下到上」四条边的遍历,并在每步之后检查边界是否交错,以判断是否遍历完毕。
- 循环终止条件 :当某一步收缩后
top > bottom或left > right,说明所有元素已访问完毕,立即结束循环。 - 空间压缩 :整个过程只使用常数个边界变量,不额外开辟与矩阵规模相关的存储,空间复杂度为 O ( 1 ) O(1) O(1)。
算法变体与扩展:
- 逆时针螺旋遍历:只需调整四条边的遍历顺序(从「从上到下」开始),即可实现逆时针方向的螺旋访问,思路完全对称。
- 矩形子区域遍历:当需要遍历矩阵中某个子矩形区域时,只需将四个边界指针初始化为该子区域的坐标范围,其余逻辑不变。
- 分层递归写法:也可以用递归的方式逐层处理,每层剥掉最外圈后递归处理内层子矩阵,代码更简洁但需注意递归深度。
- 边界处理:对于只有一行、一列或空矩阵的特殊情况,必须在循环开始前或每步收缩后做好越界判断,否则容易越界访问。
与其他算法的对比:
- 方向模拟法 : O ( m × n ) O(m \times n) O(m×n) 时间、 O ( 1 ) O(1) O(1) 空间,用方向数组 + 边界判断控制转向,思路直观但需要额外的方向状态管理。
- 边界收缩法 : O ( m × n ) O(m \times n) O(m×n) 时间、 O ( 1 ) O(1) O(1) 空间,通过边界指针收缩天然避免重复访问,代码结构更清晰,是本题最优解。
- 访问标记数组 : O ( m × n ) O(m \times n) O(m×n) 时间、 O ( m × n ) O(m \times n) O(m×n) 空间,用布尔数组记录已访问位置,思路最简单但空间开销大,不满足本题 O ( 1 ) O(1) O(1) 额外空间要求。
相关 LeetCode 例题:
- 54. 螺旋矩阵(本题,边界收缩法)
- 59. 螺旋矩阵 II(按螺旋顺序填充矩阵,思路对称)
- 48. 旋转图像(原地矩阵变换,涉及边界交换)
- 73. 矩阵置零(矩阵遍历与原地标记)


