给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素
示例 1:
输入:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
输出: [1,2,3,6,9,8,7,4,5]
示例 2:
输入:
[
[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行n列的数组,循环的话完整的圈数有m、n中最小值的一半,是否有不完整的循环靠对2取余得到,将遍历分为→↓←↑四趟,这四趟的坐标移动规律靠画图很容易整理出:
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List list = new ArrayList<Integer>();
if(matrix == null || matrix.length == 0) {
return list;
}
int q = 0;
int m = matrix.length;
int n = matrix[0].length;
if(m < n) {
q = m / 2 + m % 2 ;
} else {
q = n / 2 + n % 2 ;
}
for(int i = 0 ; i < q ; i++) {
for(int a = i; a < n - i; a++) {
list.add(matrix[i][a]);
}
for(int b = i + 1; b < m - i; b ++) {
list.add(matrix[b][n-1-i]);
}
for(int c = n - 2 -i; c >= i && i * 2 + 1 < m; c--) {
list.add(matrix[m-1-i][c]);
}
for(int d = m - 2- i; d >= i+1 && i * 2 + 1 < n; d--) {
list.add(matrix[d][i]);
}
}
return list;
}
}
二、模拟:
模拟数组螺旋遍历的过程,使用dr、dc分别表示右下左上螺旋循环时x/y坐标移动的增量,然后循环比例,一但走到数组边界,或者下个元素已经被遍历过了,则更换方向,继续遍历。
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List ans = new ArrayList();
if (matrix.length == 0) return ans;
int R = matrix.length, C = matrix[0].length;
boolean[][] seen = new boolean[R][C];
int[] dr = {0, 1, 0, -1};
int[] dc = {1, 0, -1, 0};
int r = 0, c = 0, di = 0;
for (int i = 0; i < R * C; i++) {
ans.add(matrix[r][c]);
seen[r][c] = true;
int cr = r + dr[di];
int cc = c + dc[di];
if (0 <= cr && cr < R && 0 <= cc && cc < C && !seen[cr][cc]){
r = cr;
c = cc;
} else {
di = (di + 1) % 4;
r += dr[di];
c += dc[di];
}
}
return ans;
}
}
时间复杂度: O(N),其中 N 是输入矩阵所有元素的个数。因为我们将矩阵中的每个元素都添加进答案里。
空间复杂度: O(N),需要两个矩阵 seen 和 ans 存储所需信息。
三、按层模拟
方法和上边的思路一样,换了实现
class Solution {
public List < Integer > spiralOrder(int[][] matrix) {
List ans = new ArrayList();
if (matrix.length == 0)
return ans;
int r1 = 0, r2 = matrix.length - 1;
int c1 = 0, c2 = matrix[0].length - 1;
while (r1 <= r2 && c1 <= c2) {
for (int c = c1; c <= c2; c++) ans.add(matrix[r1][c]);
for (int r = r1 + 1; r <= r2; r++) ans.add(matrix[r][c2]);
if (r1 < r2 && c1 < c2) {
for (int c = c2 - 1; c > c1; c--) ans.add(matrix[r2][c]);
for (int r = r2; r > r1; r--) ans.add(matrix[r][c1]);
}
r1++;
r2--;
c1++;
c2--;
}
return ans;
}
}
时间、空间复杂度O(N)
四、旋转数组
读取第一行,然后旋转剩下的数组,直到数组被裁减没
五、其他的实现方式
首先设定上下左右边界
其次向右移动到最右,此时第一行因为已经使用过了,可以将其从图中删去,体现在代码中就是重新定义上边界
判断若重新定义后,上下边界交错,表明螺旋矩阵遍历结束,跳出循环,返回答案
若上下边界不交错,则遍历还未结束,接着向下向左向上移动,操作过程与第一,二步同理
不断循环以上步骤,直到某两条边界交错,跳出循环,返回答案
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector <int> ans;
if(matrix.empty()) return ans; //若数组为空,直接返回答案
int u = 0; //赋值上下左右边界
int d = matrix.size() - 1;
int l = 0;
int r = matrix[0].size() - 1;
while(true)
{
for(int i = l; i <= r; ++i) ans.push_back(matrix[u][i]); //向右移动直到最右
if(++ u > d) break; //重新设定上边界,若上边界大于下边界,则遍历遍历完成,下同
for(int i = u; i <= d; ++i) ans.push_back(matrix[i][r]); //向下
if(-- r < l) break; //重新设定有边界
for(int i = r; i >= l; --i) ans.push_back(matrix[d][i]); //向左
if(-- d < u) break; //重新设定下边界
for(int i = d; i >= u; --i) ans.push_back(matrix[i][l]); //向上
if(++ l > r) break; //重新设定左边界
}
return ans;
}
};