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那个太扯了,谁会去这么想。。。。感觉这个比较接地气

1
附上逻辑很漂漂的代码

~

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确实麻烦一些,第一他一开始是空的,而且不能访问越界,不像数组,很容易定义一个很大的数组。

Posted by richard爱闹 - 7月 15 2014
如需转载,请注明: 本文来自 Richard