一、题目描述

二、解题思路
我们使用边界收缩法来解决这个问题:
-
定义四个边界:上边界(u)、下边界(d)、左边界(l)、右边界(r)
-
按照顺时针方向遍历:右→下→左→上
-
每完成一个方向的遍历,就收缩相应的边界
-
重复直到所有元素都被遍历
三、完整代码
cpp
class Solution { public: vector<int> spiralOrder(vector<vector<int>>& matrix) { vector<int> rst; int rol = matrix.size(), col = matrix[0].size(); // 获取矩阵行列数 int l = 0, r = col - 1, u = 0, d = rol - 1; // 初始化边界 while (l <= r && u <= d) { // 从左到右遍历上边界 for (int j = l; j <= r; j++) { rst.push_back(matrix[u][j]); } u++; // 上边界下移 // 从上到下遍历右边界 for (int j = u; j <= d; j++) { rst.push_back(matrix[j][r]); } r--; // 右边界左移 // 从右到左遍历下边界(需要检查是否还有行) if (u <= d) { for (int j = r; j >= l; j--) { rst.push_back(matrix[d][j]); } d--; // 下边界上移 } // 从下到上遍历左边界(需要检查是否还有列) if (l <= r) { for (int j = d; j >= u; j--) { rst.push_back(matrix[j][l]); } l++; // 左边界右移 } } return rst; } };
四、代码解析
1. 获取矩阵行列数
int rol = matrix.size(), col = matrix[0].size();
-
matrix.size():返回二维向量的行数(row count)-
matrix是一个vector<vector<int>>,每个元素是一个vector<int>(一行) -
matrix.size()返回外层向量的大小,即矩阵的行数
-
-
matrix[0].size():返回第一行的列数(column count)-
matrix[0]是第一行的向量(索引为0) -
matrix[0].size()返回该向量的元素个数,即矩阵的列数
-
2. 边界初始化
int l = 0, r = col - 1, u = 0, d = rol - 1;
利于后续直接行列索引定位!
-
l:当前未遍历的最左列索引 -
r:当前未遍历的最右列索引 -
u:当前未遍历的最上行索引 -
d:当前未遍历的最下行索引
3. 整体循环条件
while (l <= r && u <= d)
-
l <= r:还有列需要遍历
-
u <= d:还有行需要遍历
-
两者都满足时,矩阵中还有元素未遍历
4. 顺时针遍历详细说明
第一方向:从左到右(上边界)
for (int j = l; j <= r; j++) {
rst.push_back(matrix[u][j]); // 固定行u,列从l到r
}
u++; // 上边界下移:上面这一行已经全部遍历完
-
遍历当前最上面一行的所有列
-
遍历完后,这一行永远不会再被访问,所以u++
第二方向:从上到下(右边界)
for (int j = u; j <= d; j++) {
rst.push_back(matrix[j][r]); // 固定列r,行从u到d
}
r--; // 右边界左移:右边这一列已经全部遍历完
-
遍历当前最右边一列的所有行(不包括已遍历的角点)
-
注意起始是 j=u 而不是 j=u+1,因为 u 已经增加了
-
遍历完后,这一列永远不会再被访问,所以r--
第三方向:从右到左(下边界)
if (u <= d) { // 重要:检查是否还有行需要遍历
for (int j = r; j >= l; j--) {
rst.push_back(matrix[d][j]); // 固定行d,列从r到l
}
d--; // 下边界上移:下面这一行已经全部遍历完
}
-
为什么需要
if (u <= d)判断?-
对于窄矩阵(列数>行数)的最后几轮,可能只剩下单行,可以通过判断进入while循环,但在从左到右遍历过之后已经遍历了整个矩阵,从上到下不会有影响,但如果再次从右到左遍历,就会重复添加同一行元素。
-
例如:3×1的矩阵,只有一列,不需要从右到左遍历
-
第四方向:从下到上(左边界)
if (l <= r) { // 重要:检查是否还有列需要遍历
for (int j = d; j >= u; j--) {
rst.push_back(matrix[j][l]); // 固定列l,行从d到u
}
l++; // 左边界右移:左边这一列已经全部遍历完
}
五、语法要点
1.二维数组大小
vector<vector<int>> matrix; // 二维向量,可以看作矩阵
内存结构理解:
matrix = [
1, 2, 3\], // matrix\[0\] 是一个 vector\
\[4, 5, 6\], // matrix\[1\] 是一个 vector\ \[7, 8, 9\] // matrix\[2\] 是一个 vector\
访问元素的方式:
matrix[i][j] // 访问第i行第j列的元素
matrix[i] // 获取第i行的整个向量([1,2,3])
获取行列数:
// 获取行数(二维数组外层大小=行数)
int rows = matrix.size();
// 获取列数(二维数组取第一内层大小=列数)
int cols = matrix[0].size(); // 返回3
// 安全获取列数(先检查是否为空)
int cols = 0;
if (!matrix.empty()) {
cols = matrix[0].size();
}
六、执行示例
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
执行过程:
-
初始化:l=0, r=2, u=0, d=2
-
第一轮循环:
-
上边界:1,2,3 → u=1
-
右边界:6,9 → r=1
-
下边界:8,7 (u=1 ≤ d=2) → d=1
-
左边界:4 (l=0 ≤ r=1) → l=1
-
-
第二轮循环:
-
上边界:5 (l=1 ≤ r=1) → u=2
-
右边界:(u=2 > d=1) 不执行
-
下边界:(u=2 ≤ d=1) 不成立,不执行
-
左边界:(l=1 ≤ r=1) 但 u=2 > d=1,不执行
-
-
循环结束
输出结果 :[1,2,3,6,9,8,7,4,5]
七、总结
本文介绍了螺旋矩阵的顺时针遍历算法。通过定义四个边界并使用边界收缩法,我们可以按照螺旋顺序遍历矩阵中的所有元素。算法的关键在于理解二维向量的结构:matrix.size()获取行数,matrix[0].size()获取列数(假设矩阵非空)。这种边界收缩法不仅思路清晰,而且代码简洁高效,时间复杂度为O(m×n),空间复杂度为O(1)(不考虑输出结果的空间)。