一、题目要求
- 给你一个 m 行 n 列的矩阵 matrix ,请按照顺时针螺旋顺序 ,返回矩阵中的所有元素。
- 示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

输入: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]
- 提示:
-
-
-
-
- -100 <= matrix[i][j] <= 100
二、求解算法
① 按圈遍历
- 从外部向内部逐层遍历打印矩阵,最外面一圈打印完,里面仍然是一个矩阵。
- 统计矩阵的层数,每一层最多会占据两行或者两列,最少会占据一行或者一列元素,只有一层或者一列,也算一层,分层如下图:

int m = matrix.length;
int n = matrix[0].length;
int count = (Math.min(m, n)+1)/2;
- 开始打印第 i 层的矩阵元素,如下图所示,再打印第 i 层的矩阵时,要经历 4 个循环:

for (int j = i; j < n-i; j++) {
list.add(matrix[i][j]);
}
for (int j = i+1; j < m-i; j++) {
list.add(matrix[j][(n-1)-i]);
}
- 第 3 个:从右往左,如果这一层只有1行,那么第一个循环已经将该行打印,这里就不需要打印了,即 (m-1-i )!= i:
for (int j = (n-1)-(i+1); j >= i && (m-1-i != i); j--) {
list.add(matrix[(m-1)-i][j]);
}
- 第4个:从下往上,如果这一层只有1列,那么第2个循环已经将该列打印了,这里不需要打印,即(n-1-i) != i
for (int j = (m-1)-(i+1); j >= i+1 && (n-1-i) != i; j--) {
list.add(matrix[j][i]);
}
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> list = new ArrayList<Integer>();
if(matrix == null || matrix.length == 0)
return list;
int m = matrix.length;
int n = matrix[0].length;
int i = 0;
int count = (Math.min(m, n)+1)/2;
while(i < count) {
for (int j = i; j < n-i; j++) {
list.add(matrix[i][j]);
}
for (int j = i+1; j < m-i; j++) {
list.add(matrix[j][(n-1)-i]);
}
for (int j = (n-1)-(i+1); j >= i && (m-1-i != i); j--) {
list.add(matrix[(m-1)-i][j]);
}
for (int j = (m-1)-(i+1); j >= i+1 && (n-1-i) != i; j--) {
list.add(matrix[j][i]);
}
i++;
}
return list;
}
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
int sizey = matrix.size();
int sizex = matrix[0].size();
int loop = min(sizey, sizex) / 2;
int starty = 0, startx = 0;
int leny = sizey - 1, lenx = sizex - 1;
vector<int> ans;
if(sizey == 1) {
for(int x = startx, y = starty; x < startx + lenx + 1; x ++) {
ans.emplace_back(matrix[y][x]);
}
return ans;
}
if(sizex == 1) {
for(int x = startx, y = starty; y < starty + leny + 1; y ++) {
ans.emplace_back(matrix[y][x]);
}
return ans;
}
while(loop --) {
for(int x = startx, y = starty; x < startx + lenx; x ++) {
ans.emplace_back(matrix[y][x]);
}
for(int x = startx + lenx, y = starty; y < starty + leny; y ++) {
ans.emplace_back(matrix[y][x]);
}
for(int x = startx + lenx, y = starty + leny; x > startx; x --) {
ans.emplace_back(matrix[y][x]);
}
for(int x = startx, y = starty + leny; y > starty; y --) {
ans.emplace_back(matrix[y][x]);
}
starty ++;
startx ++;
leny -= 2;
lenx -= 2;
}
if(sizey <= sizex && sizey % 2 == 1) {
for(int x = startx, y = starty; x < startx + lenx + 1; x ++) {
ans.emplace_back(matrix[y][x]);
}
}
if(sizey > sizex && sizex % 2 == 1) {
for(int x = startx, y = starty; y < starty + leny + 1; y ++) {
ans.emplace_back(matrix[y][x]);
}
}
return ans;
}
};
② 模拟
- 可以模拟螺旋矩阵的路径。初始位置是矩阵的左上角,初始方向是向右,当路径超出界限或者进入之前访问过的位置时,顺时针旋转,进入下一个方向。
- 判断路径是否进入之前访问过的位置需要使用一个与输入矩阵大小相同的辅助矩阵 visited,其中的每个元素表示该位置是否被访问过。当一个元素被访问时,将 visited 中的对应位置的元素设为已访问。
- 如何判断路径是否结束?由于矩阵中的每个元素都被访问一次,因此路径的长度即为矩阵中的元素数量,当路径的长度达到矩阵中的元素数量时即为完整路径,将该路径返回。
- Java 示例:
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> order = new ArrayList<Integer>();
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return order;
}
int rows = matrix.length, columns = matrix[0].length;
boolean[][] visited = new boolean[rows][columns];
int total = rows * columns;
int row = 0, column = 0;
int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
int directionIndex = 0;
for (int i = 0; i < total; i++) {
order.add(matrix[row][column]);
visited[row][column] = true;
int nextRow = row + directions[directionIndex][0], nextColumn = column + directions[directionIndex][1];
if (nextRow < 0 || nextRow >= rows || nextColumn < 0 || nextColumn >= columns || visited[nextRow][nextColumn]) {
directionIndex = (directionIndex + 1) % 4;
}
row += directions[directionIndex][0];
column += directions[directionIndex][1];
}
return order;
}
}
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);
int row = 0, column = 0;
int directionIndex = 0;
for (int i = 0; i < total; i++) {
order[i] = matrix[row][column];
visited[row][column] = true;
int nextRow = row + directions[directionIndex][0], nextColumn = column + directions[directionIndex][1];
if (nextRow < 0 || nextRow >= rows || nextColumn < 0 || nextColumn >= columns || visited[nextRow][nextColumn]) {
directionIndex = (directionIndex + 1) % 4;
}
row += directions[directionIndex][0];
column += directions[directionIndex][1];
}
return order;
}
};
- 复杂度分析:
-
- 时间复杂度:O(mn),其中 m 和 n 分别是输入矩阵的行数和列数。矩阵中的每个元素都要被访问一次。
-
- 空间复杂度:O(mn)。需要创建一个大小为 m×n 的矩阵 visited 记录每个位置是否被访问过。
③ 按层模拟(LeetCode 官方解法)
- 可以将矩阵看成若干层,首先输出最外层的元素,其次输出次外层的元素,直到输出最内层的元素。
- 定义矩阵的第 k 层是到最近边界距离为 k 的所有顶点。例如,如下矩阵最外层元素都是第 1 层,次外层元素都是第 2 层,剩下的元素都是第 3 层:
[[1, 1, 1, 1, 1, 1, 1],
[1, 2, 2, 2, 2, 2, 1],
[1, 2, 3, 3, 3, 2, 1],
[1, 2, 2, 2, 2, 2, 1],
[1, 1, 1, 1, 1, 1, 1]]
- 对于每层,从左上方开始以顺时针的顺序遍历所有元素。假设当前层的左上角位于 (top,left),右下角位于 (bottom,right),按照如下顺序遍历当前层的元素。
-
- 从左到右遍历上侧元素,依次为 (top,left) 到 (top,right)。
-
- 从上到下遍历右侧元素,依次为 (top+1,right) 到 (bottom,right)。
-
- 如果 left<right 且 top<bottom,则从右到左遍历下侧元素,依次为 (bottom,right−1) 到 (bottom,left+1),以及从下到上遍历左侧元素,依次为 (bottom,left) 到 (top+1,left)。
- 遍历完当前层的元素之后,将 left 和 top 分别增加 1,将 right 和 bottom 分别减少 1,进入下一层继续遍历,直到遍历完所有元素为止。

class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> order = new ArrayList<Integer>();
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return order;
}
int rows = matrix.length, columns = matrix[0].length;
int left = 0, right = columns - 1, top = 0, bottom = rows - 1;
while (left <= right && top <= bottom) {
for (int column = left; column <= right; column++) {
order.add(matrix[top][column]);
}
for (int row = top + 1; row <= bottom; row++) {
order.add(matrix[row][right]);
}
if (left < right && top < bottom) {
for (int column = right - 1; column > left; column--) {
order.add(matrix[bottom][column]);
}
for (int row = bottom; row > top; row--) {
order.add(matrix[row][left]);
}
}
left++;
right--;
top++;
bottom--;
}
return order;
}
}
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 left = 0, right = columns - 1, top = 0, bottom = rows - 1;
while (left <= right && 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 (left < right && 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]);
}
}
left++;
right--;
top++;
bottom--;
}
return order;
}
};
三、博客之星
- 今年是我第一次参加博客之星,需要各位大佬的支持,麻烦百忙之中,抽出一点宝贵的时间,给我投一下票:https://bbs.csdn.net/topics/603955258 给我一个五星(列表会一一全部回复),不胜感激!