给你一个 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
解法一:
// 解决方案类定义
class Solution {
// 私有静态常量成员,表示四个方向(右、下、左、上)
private:
static constexpr int directions[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
// 公有成员函数,用于返回矩阵的顺时针螺旋遍历结果
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
// 如果矩阵为空或只有一行一列,则返回空向量
if (matrix.size() == 0 || matrix[0].size() == 0)
return {};
// 获取矩阵的行数和列数
int rows = matrix.size(), columns = matrix[0].size();
// 创建一个与矩阵大小相同的布尔矩阵,用来标记哪些单元格已经被访问过
vector<vector<bool>> visited(rows, vector<bool>(columns));
// 计算矩阵中的总元素个数
int total = rows * columns;
// 创建一个与矩阵大小相同的向量,用于存储遍历结果
vector<int> order(total);
// 初始化行和列索引为0,方向索引为0(表示向右移动)
int row = 0, column = 0, directionsIndex = 0;
// 遍历每个单元格
for (int i = 0; i < total; i++) {
// 将当前单元格的值添加到结果向量中
order[i] = matrix[row][column];
// 标记当前单元格为已访问
visited[row][column] = true;
// 计算下一个单元格的行和列索引
int nextRow = row + directions[directionsIndex][0],
nextColumn = column + directions[directionsIndex][1];
// 检查下一个单元格是否越界或已被访问
if (nextRow < 0 || nextRow >= rows || nextColumn < 0 ||
nextColumn >= columns || visited[nextRow][nextColumn])
// 如果越界或已访问,改变方向索引,并重新计算下一个单元格的索引
directionsIndex = (directionsIndex + 1) % 4;
// 更新行和列索引
row += directions[directionsIndex][0];
column += directions[directionsIndex][1];
}
// 返回螺旋遍历的结果
return order;
}
};
首先设置一个静态常量成员directions,目的是通过他更改方向,里面每一个成员的第一个代表行变化量,第二个代表列变化量。
进入目标函数,首先判断矩阵是否为空,如果矩阵的行为空或者列为空则返回空数组。然后用rows和columns保存矩阵的行数和列数。再创建一个与矩阵大小相同的布尔矩阵,用来记录矩阵的该点是否被访问过,用行乘以宽计算矩阵的总元素个数,再创建一个含有总元素个数大小的数组用来记录访问过程,也就是答案。先初始化行、列、方向索引都为0,用for循环遍历存储答案的元素,依次给他填满元素,每添加一个元素进入我们就把这个元素在矩阵的相应位置在布尔矩阵里面设置为真,表示这个位置的元素已经访问过了,然后再计算下一个位置的索引,新行的索引是加上本方向上的第一个元素,新列的索引是加上本方向上的第二个元素。然后再检查新行新列是否越界,如果越界了就顺时针更改方向,也就是方向索引增加,但是为了不超过上下左右这四个方向的大小、也就是4,所以要%4,这样就会在这四个方向实现一直更新循环,保持顺时针方向。我们已经判断号新的方向之后,再更新新行新列。这里注意不能直接将新行新列设置为判断是否要变方向前的新行新列,否则会导致错误。要将其从新计算。最后return遍历结果。
解法二:
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if (matrix.size() == 0 || matrix[0].size() == 0)
return {};
int rows = matrix.size(), columns = matrix[0].size();
vector<int> order;
int right = columns - 1, left = 0, bottom = rows - 1, top = 0;
while (right >= left && top <= bottom) {
for (int column = left; column <= right; column++) {
order.push_back(matrix[top][column]);
}
for (int row = top + 1; row <= bottom; row++) {
order.push_back(matrix[row][right]);
}
if (right > left && top < bottom) {
for (int column = right - 1; column > left; column--)
order.push_back(matrix[bottom][column]);
for (int row = bottom; row > top; row--)
order.push_back(matrix[row][left]);
}
right--;
left++;
top++;
bottom--;
}
return order;
}
};
这个方法相对于解法一来说更好理解,就和剥洋葱一样,一层一层的遍历最外围的数,直到全部都遍历完。首先判断行或者列是否为空,不为空就继续,计算出矩阵的行数和列数并存储,创建一个容器存储遍历的答案,right指向当前层数的最右边,left指向当前层数的最左边,top指向当前层的顶端,bottom指向当前层的最低端。当当前层还有数时进入循环。首先从左往右遍历最顶层,然后从上往下遍历最右层,在这里要判断是否只有一层或者一列,如果只有一层或者一列就不用再往左往上遍历了,防止重复计算结果。在还有多余的行和列的时候,从右往左遍历最底层,从下往上遍历最左层,完成了当前最外层的遍历,然后调整一下使得下一次遍历遍历里面一层。最终返回结果。