MatrixSpiral
顺时针打印,看起来是普通的程设题,不需要复杂的数据结构,但是还是会遇到很多问题,边界和循环很复杂。
我的思路就是一次打印一圈,然后往里循环,直到里面没有了为止,但是例如第一行是全部打印,还是留一个,因此有两种方案,我觉得留一个这种可以保持四条边统一,所以选择这种,
事实好像前者的答案代码会更加清晰o(╯□╰)o
然后先写最外层的4个循环,然后试图加个level参数k进4个loop,倒不难,写出了伪代码,
k=0
while()
{
for(i=1+k,j=1+k->n-1-k) ++
for(j=n-k,i=1+k->m-1-k) ++
for(i=m-k,j=n-k->2+k) --
for(j=1+k,i=m-k->2+k) --
k++;
}
但是测试的时候,发现3_3 5_5这种 矩阵中心的打印不到,后来发现了一个问题,就是最后剩的一行,一列这种,于是总结下行的时候如果行数
等于上行的,也即上下重叠,说明下行没元素了,显然右列也没元素了。右列也是如此,于是加了一下逻辑判断的代码,并且最早也漏了循环结束条件,这里正好补上,以为对了= =
k=0
while()
{
for(i=1+k,j=1+k->n-1-k) ++
for(j=n-k,i=1+k->m-1-k) ++
if(m-k==1+k)
{
spiralnum.push_back(matrix.at(1+k-1).at(n-k-1));
break;
}
for(i=m-k,j=n-k->2+k) --
if(1+k==n-k)
{
spiralnum.push_back(matrix.at(m-k-1).at(n-k-1));
break;
}
for(j=1+k,i=m-k->2+k) --
k++;
}
后来发现,4*4的死循环了,于是发现终止未必是下行和上行重合,或者右列和左列重合,也可能是下行是上行的前一行,或者右列是左列右一列,而且几个case总结出等的时候才会漏掉最后一个元素,
于是加上如下判断
k=0
while()
{
for(i=1+k,j=1+k->n-1-k) ++
for(j=n-k,i=1+k->m-1-k) ++
if(m-k<=1+k)
{
if(m-k==1+k)
spiralnum.push_back(matrix.at(1+k-1).at(n-k-1));
break;
}
for(i=m-k,j=n-k->2+k) --
if(1+k>=n-k)
{
if(1+k==n-k)
spiralnum.push_back(matrix.at(m-k-1).at(n-k-1));
break;
}
for(j=1+k,i=m-k->2+k) --
k++;
}
提交leetcode 发现还是WA了= = 3_2的case多打印了一个数,后来发现,还可能是 上行的时候就没元素了,于是想再次改,发现怕前面的case又过不了,想到万能方法,直接每次for看是否vector是否装满了,
于是改的以下逻辑混乱的代码,AC了= =
vector<int> spiralOrder(vector<vector<int> > &matrix) {
int m=matrix.size();//row num
vector<int> spiralnum;
if(m<=0) return spiralnum;
int n=matrix.at(0).size();//column num
if(n<=0) return spiralnum;
int k=0;//level id
int i,j;
while(1)
{
if(spiralnum.size()==m*n) break;
for(i=1+k,j=1+k;j<=n-1-k;j++)
spiralnum.push_back(matrix.at(i-1).at(j-1));
if(spiralnum.size()==m*n) break;
for(j=n-k,i=1+k;i<=m-1-k;i++)
spiralnum.push_back(matrix.at(i-1).at(j-1));
if(spiralnum.size()==m*n) break;
if(m-k<=1+k)
{
if(m-k==1+k)
spiralnum.push_back(matrix.at(1+k-1).at(n-k-1));
break;
}
for(i=m-k,j=n-k;j>=2+k;j--)
spiralnum.push_back(matrix.at(i-1).at(j-1));
if(1+k>=n-k)
{
if(1+k==n-k)
spiralnum.push_back(matrix.at(m-k-1).at(n-k-1));
break;
}
for(j=1+k,i=m-k;i>=2+k;i--)
spiralnum.push_back(matrix.at(i-1).at(j-1));
k++;
}
return spiralnum;
}
后来看了剑指Offer,他是上行全部print,包括最后一个。而且要一开始想清楚,我想到后面,感觉好像就漏了几个case,再改可能就像一个project一样,看起来一个bug,一改,出来几千个o(╯□╰)o
逻辑已经混乱了,写这种行列的代码想起了我当年设计五子棋AI的VB代码,也是改了好多趟,最后还是有bug,只不过看起来还不错了。。。。。
另外发现其实自己有个不好的习惯,费了劲写完代码后,不太愿意用各种情况都包含的case去测试,这个是以后软件测试很重要的,要先想好,分为哪些测试用例。
最后,我看了下剑指Offer的思路 写了一个逻辑比较漂漂的MatrixSpiral算法。后面debug发现 自己右列的 for循环 rowend 写成了colend,结果测试用例 只有最后一个case差了一个元素,这简直是一个奇迹!!
我重新 总结了下算法思路,按照剑指Offer的,每一次(一行一列)不是 我之前想的都留一个元素,而是有的都print。按照书上的,每一次rowstart=colstart,即为start,
然后colend=col-1-start, rowend=row-1-start
于是四个循环有如下的伪代码,这里上行保证至少一行就够了,start<=rowend, 右列也是保证一列可以了,不会print错,如果一列镁元素(例如一个元素被作为行print了),后面for会控制好的。
if(start<=rowend)//at least one row, maybe none number left
{
for(int j=start;j<=colend;j++)//row: start col: start->colend, if col has num, start must be valid
spiralnum.push_back(matrix.at(start).at(j));
}
if(start<=colend)//at least one row
{
for(int i=start+1;i<=rowend;i++)//col: colend row:start+1->colend
spiralnum.push_back(matrix.at(i).at(colend));
}
if(start+1<=rowend)//at least two rows
{
for(int j=colend-1;j>=start;j--)//row: rowend col: colend-1->start
spiralnum.push_back(matrix.at(rowend).at(j));
}
if(start+1<=colend)//at least two rows
{
for(int i=rowend-1;i>=start+1;i--)//col: start row: rowend-1->start+1
spiralnum.push_back(matrix.at(i).at(start));
}
但是注意,下行要保证至少两行,否则会重复print上行print的,左列也是一样,start+1<=rowend, 以及start+1<=colend, 我习惯<=,因为比较清晰范围,<的话大脑还要再计算一次。
那么这是每一次的level 4个for print 怎么表示结束呢? 我后来总结,如果一行 或者一列 都没有了,就结束了,故
if(start>colend || start> rowend)
break;
剑指Offer那个太扯了,谁会去这么想。。。。感觉这个比较接地气
|
|
~
vector<int> spiralOrder_Offer(vector<vector<int> > &matrix)
{
int row=matrix.size();//row num
vector<int> spiralnum;
if(row<=0) return spiralnum;
int col=matrix.at(0).size();//column num
if(col<=0) return spiralnum;
int start=0;//row start, and col start
//bool PrintNum=false;
while(1)
{
int colend=col-1-start;
int rowend=row-1-start;
if(start>colend || start> rowend)
break;
if(start<=rowend)//at least one row, maybe none number left
{
for(int j=start;j<=colend;j++)//row: start col: start->colend, if col has num, start must be valid
spiralnum.push_back(matrix.at(start).at(j));
}
if(start<=colend)//at least one row
{
for(int i=start+1;i<=rowend;i++)//col: colend row:start+1->colend
spiralnum.push_back(matrix.at(i).at(colend));
}
if(start+1<=rowend)//at least two rows
{
for(int j=colend-1;j>=start;j--)//row: rowend col: colend-1->start
spiralnum.push_back(matrix.at(rowend).at(j));
}
if(start+1<=colend)//at least two rows
{
for(int i=rowend-1;i>=start+1;i--)//col: start row: rowend-1->start+1
spiralnum.push_back(matrix.at(i).at(start));
}
start++;
}
return spiralnum;
}
于是乎乘热打铁,把另一道相关的也做了,有点类似于反函数,之前是根据matrix 顺时针螺旋打印num,现在是已知num顺序,把他顺时针螺旋顺序赋给matrix,只有稍微改改就行了问题不大。
vector<vector<int> > generateMatrix(int n)
{
//int row=matrix.size();//row num
vector<vector<int> > matrix(n,n);
//vector<int> spiralnum;
//if(row<=0) return spiralnum;
//int col=matrix.at(0).size();//column num
//if(col<=0) return spiralnum;
int row=n,col=n;
int start=0;//row start, and col start
//bool PrintNum=false;
int k=1;
while(1)
{
int colend=col-1-start;
int rowend=row-1-start;
if(start>colend || start> rowend)
break;
if(start<=rowend)//at least one row, maybe none number left
{
for(int j=start;j<=colend;j++)//row: start col: start->colend, if col has num, start must be valid
{
matrix.at(start).at(j)=k;
k++;
//spiralnum.push_back(matrix.at(start).at(j));
}
}
if(start<=colend)//at least one row
{
for(int i=start+1;i<=rowend;i++)//col: colend row:start+1->colend
{
matrix.at(i).at(colend)=k;
k++;
//spiralnum.push_back(matrix.at(i).at(colend));
}
}
if(start+1<=rowend)//at least two rows
{
for(int j=colend-1;j>=start;j--)//row: rowend col: colend-1->start
{
matrix.at(rowend).at(j)=k;
k++;
//spiralnum.push_back(matrix.at(rowend).at(j));
}
}
if(start+1<=colend)//at least two rows
{
for(int i=rowend-1;i>=start+1;i--)//col: start row: rowend-1->start+1
{
matrix.at(i).at(start)=k;
k++;
//spiralnum.push_back(matrix.at(i).at(start));
}
}
start++;
}
return matrix;
}
这个问题倒不是太大,只要放个k进去,从1到n^2就可以了,但是我遇到了一个二维vector初始化的诡异问题,而且这个之前没有用过,好发现了VS与GCC, GCC3.2.3和4.4.7之间的微妙区别。
我先是这么弄的
vector<vector<int>> matrix(n,n);
VS10过了,提交,出现Compile error,提示两个>>离得太近,于是改成
vector<vector<int> > matrix(n,n);
还是编译错误,还是这行的问题,于是拿windows下的gcc3.2.3测,也过了,有点不解。然后拿到Linux下的另一个版本GCC4.4.7,出现error了,果然GCC不同版本都会有问题,
这个也不是C++11新增的,于是查看帖子
http://www.cnblogs.com/wei-li/archive/2012/06/08/2541576.html
发现要这么初始化二维vector
vector<vector<int> > matrix(n,vector<int>(n));
也即二维vector只有装入n个 一维的vector的 构造函数参数列表,所以改了提交。
vector确实麻烦一些,第一他一开始是空的,而且不能访问越界,不像数组,很容易定义一个很大的数组。