采用按层模拟顺时针分析如下:
1.考虑何时停止顺时针遍历:
由于是按层数start进行遍历,顺时针的起点为(start,start),可以尝试画一下3X3,4X4的矩阵,可以发现循环可以继续的条件是,列数>2*start,对行同样成立。
2.接着考虑顺时针遍历写法,主要考虑边界条件,这里cv剑指offer的分析:
首先说明顺时针打印的规则,第一次打印最上面一行,从左至右(start,[start,endCol])全部打印,第二次打印从上至下([start+1,endRow],endCol),第三次打印最下面一行,从右至左(endRow,[endCol-1,start]),第四次从下至上,([endRow-1,start+1],start)
至于每次要打印的行列的终点:
endRow=总行数-1-start
endCol=总列数-1-start
接下来对应图中的三种特殊情况做边界分析
1.从左至右一行:此时只要打印第一行即可,由于我们的打印方式,所以无论如何第一行是肯定可以打印的,不需要判断条件。
2.从上至下一列:第二张图其实是告诉我们什么情况下才能打印从上至下的这一列,对比第一种特殊情况,为什么第一种情况不能打印从上至下的一列呢,从区间可以看出,这一列的坐标是([start+1,endRow],endCol),因此我们要保证start<endRow才行,若取等于,则是第一种特殊情况,小于保证了至少有从上至下的一种情况,看第三个图也可知晓。
3.从右至左一行:对比图2图3,我们知道要想能打印这一行,根据第三个区间(endRow,[endCol-1,start])来看,是由于区间长度(endCol-1-start)+1>=1才有机会打印即start<endCol,并且还有一个前提是要能够打印从上至下的一列即start<endRow,否则可能退化成图1的情况。
4.从下至上一列,同样可由区间长度endRow-1-(start+1)+1>=1可得出打印条件start<endRow-1,另外的一个前提是,可以打印从右至左的一行,否则可能退化成图2的情况,即同时满足start<endRow&&start<endCol和start<endRow-1,最后变成start<endRow-1&&start<endCol。
代码如下所示:
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if(matrix.size()==0||matrix[0].size()==0) return {};
int start=0;
vector<int> ans;
int rows=matrix.size();
int cols=matrix[0].size();
while(rows>start*2&&cols>start*2)
{
int endRow=rows-1-start;
int endCol=cols-1-start;
//从左至右
for(int i=start;i<=endCol;++i)
{
ans.push_back(matrix[start][i]);
}
//最后只有一行的情况 不用打印从上至下的一列
//可以用[start,endRow]的大小关系来判断是否需要打印
if(start<endRow)
{
for(int i=start+1;i<=endRow;++i)
{
ans.push_back(matrix[i][endCol]);
}
}
//能打印从右至左的一行的条件是
//1.能打印出从上至下的一列
//2.[start,endCol]的大小约束关系
if(start<endCol&&start<endRow)
{
for(int i=endCol-1;i>=start;--i)
{
ans.push_back(matrix[endRow][i]);
}
}
//能打印从下至上的条件是
//1.start<endCol以保证可以从左至右
//2.start<endRow-1以保证从上至下和从右至左,并且可以从下至上
if(start<endCol&&start<endRow-1)
{
for(int i=endRow-1;i>start;--i)
{
ans.push_back(matrix[i][start]);
}
}
++start;
}
return ans;
}
};