题目如下:
Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order.
For example,
Given the following matrix:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
You should return [1,2,3,6,9,8,7,4,5].
分析如下:
题目的意思是,按照顺时针方向旋转着遍历矩阵,把遍历结果返回。关键是怎么理解旋转的含义?
可以这样来思考。以上面的矩阵为例。
step one: 1,2
step two: 3,6
step three: 9,8
step four: 7,4
这样完成了对最外层的遍历,剥去一层之后(leetcode官网上说,想象剥洋葱那样剥去一层),继续往下剥,去掉次外层,不断进行下去,直到边界条件。显然边界条件是最后只剩1个点,或者一行,或者一列,导致没法拐个弯形成一个矩形。
那么边界条件是什么时候可能发生呢?这是本题最坑爹的地方了,我在这里写了很久才找到正确的边界条件判断语句。
需要写几个4*4, 4*6, 4*2, 5*5, 5*3, 5*7, 4*5, 4*3, 5*4, 5*6例子自己看看,这样才能看出来。
结合看上面的图,可以知道,
1 在rowcount 和colcount中选较小值。到底剥洋葱剥了几层由较小值决定。
2 如果较小值是奇数,那么剥洋葱到最后会产生一条单独的线段。如果较小值是偶数,然么剥洋葱到最后就会完成所有点的打印,不会留有单独的线段。
上面的做法是顺着题意思考的做法,思考边界条件的地方非常坑爹。
还有一种做法是递归,参见leetcode官网的解释,代码量顿时减少很多,虽然时间慢一些,但是本题的递归并没产生太多重复的计算,所以时间空间的开销都是可以忍的。
我的代码:
// 4ms过大集合
class Solution {
public:
vector<int> spiralOrder(vector<vector<int> > &matrix) {
vector<int> res;
int row=(int)matrix.size();
if(row==0)
return res;
int col=(int)matrix[0].size();
if(col==0)
return res;
int count=row<col?row:col;
for(int i=0;i<count/2;i++){
for(int j=i;j<col-1-i;j++)
res.push_back(matrix[i][j]);
for(int j=i;j<row-1-i;j++)
res.push_back(matrix[j][col-i-1]);
for(int j=col-1-i;j>i;j--)
res.push_back(matrix[row-1-i][j]);
for(int j=row-1-i;j>i;j--)
res.push_back(matrix[j][i]);
}
//单独处理在最后可能出现的单列或者单行的情况
if(row%2==1&&col%2==1){ //行数为奇,列数为奇,必然需要处理单行或单列
if(row<=col){
for(int i=0;i<=col-row;i++)
res.push_back(matrix[row/2][row/2+i]);
}else{
for(int i=0;i<=row-col;i++)
res.push_back(matrix[col/2+i][col/2]);
}
}else if(row%2==1&&col%2==0){ //行数为奇,列数为偶,仅当row<col才需要处理单行或单列
if(row<col){
for(int i=0;i<=col-row;i++)
res.push_back(matrix[row/2][row/2+i]);
}
}else if(row%2==0&&col%2==1){//行数为偶,列数为奇,仅当col<row才需要处理单行或单列
if(col<row){
for(int i=0;i<=row-col;i++)
res.push_back(matrix[col/2+i][col/2]);
}
}
return res;
}
};
//leetcode官网解答
void print_spiral(int mat[][N_MAX], int m, int n, int k) {
if (m <= 0 || n <= 0)
return;
if (m == 1) {
for (int j = 0; j < n; j++)
cout << mat[k][k+j] << " ";
return;
}
if (n == 1) {
for (int i = 0; i < m; i++)
cout << mat[k+i][k] << " ";
return;
}
// print from top left
for (int j = 0; j < n - 1; j++)
cout << mat[k][k+j] << " ";
// print from top right
for (int i = 0; i < m - 1; i++)
cout << mat[k+i][k+n-1] << " ";
// print from bottom right
for (int j = 0; j < n - 1; j++)
cout << mat[k+m-1][k+n-1-j] << " ";
// print from bottom left
for (int i = 0; i < m - 1; i++)
cout << mat[k+m-1-i][k] << " ";
print_spiral(mat, m-2, n-2, k+1);
}
void print_spiral_helper(int mat[][N_MAX], int m, int n) {
print_spiral(mat, m, n, 0);
}
题目小结:
(1) 这道题写了很久才写出来。首先是没有想到用递归,如果用递归,可以很快写出来。其次是,就算没有想到用递归,那么迭代版依然花了很长时间,这里面的时间大量花费在没有真正想清楚边界条件就去着急写code了。所以如果没有想清楚,不要着急写。最省时间的方式是先想清楚了再写。
update: 2015-01-10
//4ms
class Solution {
public:
vector<int> spiralOrder(vector<vector<int> > &matrix) {
vector<int> res;
int nRow = matrix.size();
if (nRow == 0) return res;
int nCol = matrix[0].size();
if (nCol == 0) return res;
if (nRow == 1) return matrix[0];
if (nCol == 1) {
for (int i = 0; i < nRow; ++i)
res.push_back(matrix[i][0]);
return res;
}
int offset_range = nRow < nCol ? nRow/2 : nCol/2;
for (int offset = 0; offset < offset_range; ++offset) {
// up
for (int i = offset; i < nCol - offset - 1; ++i) {
res.push_back(matrix[offset][i]);
}
// right
for (int i = offset; i < nRow - offset - 1; ++i) {
res.push_back(matrix[i][nCol - offset - 1]);
}
//down
for (int i = nCol - offset - 1; i >= 1 + offset; --i) {
res.push_back(matrix[nRow - offset - 1][i]);
}
//left
for (int i = nRow - offset - 1; i >= 1 + offset; --i) {
res.push_back(matrix[i][offset]);
}
}
if (nRow < nCol && nRow % 2 == 1) {
for (int i = offset_range; i < nCol - offset_range; ++i ) {
res.push_back(matrix[nRow/2][i]);
}
} else if (nRow >= nCol && nCol %2 == 1) {
for (int i = offset_range; i < nRow - offset_range; ++i) {
res.push_back(matrix[i][nCol/2]);
}
}
return res;
}
};
这道题目还有 续集 Spiral Matrix II,其实本质是同一道题目。