顺时针打印矩阵

转载自http://zhedahht.blog.163.com/

分析:第一次看到这个题目的时候,觉得这个题目很简单,完全不需要用到数据结构或者算法的知识,因此没有兴趣做这道题。后来听到包括AutodeskEMC在内的多家公司在面试或者笔试里采用过这道题,于是想这么多家公司用它来检验一个程序员的编程功底总是有原因的,于是决定自己写一遍试一下。真正写一遍才发现,要完整写出这道题的代码,还真不是件容易的事情。

自己分析:自己的想法是,在出现的这个矩阵中,需要两层循环,然后循环的方向,以及循环的边界条件不断的改变。但是事实上,写起来比较的复杂。实际上有网友是这么写的,没有仔细的去研究。何海涛的这篇算法利用的是打印的一个又一个的圈的方式:每打印一层,实际上就是打印了一圈。我们只要实现这个打印的圈的函数,剩下的交给外层,一层层的往里就行了。

但是这里需要注意几个问题: 1  终止条件,也就是边界条件。文中用的是》2start。没有很好的弄明白,因为比如说在n=9的时候,第五行的还没有打印,会不会就终止了?这个问题还没有想清楚。

2  注意的问题是,在考虑写函数的时候,要把不重要的条件暂时性的简化掉。如果有递归,就认为所有的递归都已经解决了;然后从最一般的情况开始写起,从中间写起。不是从开始,也不是从结尾。但是写完之后要注意检查是否符合开始和结尾,是否符合边界条件,是否检查了参数是否正确。

3  在打印圈的函数里面,有个比较,没有仔细看。

4 实际在书里面是没有用startx和starty的,只用了start,因为startx=starty

                解决这道题的在于代码中会包含很多个循环,而且还有多个边界条件需要判断。如果在把问题考虑得很清楚之前就开始写代码,不可避免地会越写越混乱。因此解决这个问题的关键,在于先要形成清晰的思路,把复杂的问题分解成若干个简单的问题。下面分享我分析这个问题的过程。

                通常当我们遇到一个复杂的问题的时候,我们可以用图形帮助我们思考。由于我们是以从外圈到内圈的顺序依次打印,我们在矩阵中标注一圈作为我们分析的目标。在下图中,我们设矩阵的宽度为columns,而其高度为rows。我们我们选取左上角坐标为(startX, startY),右下角坐标为(endX, endY)的一个圈来分析。

 

 

 

 

 

 

 

startX, startY

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

endX, endY

 

 

 

 

 

 

 

由于endXendY可以根据startXstartY以及columnsrows来求得,因此此时我们只需要引入startXstartY两个变量。我们可以想象有一个循环,在每一次循环里我们从(startX, startY)出发按照顺时针打印数字。

接着我们分析这个循环结束的条件。对一个5×5的矩阵而言,最后一圈只有一个数字,对应的坐标为(2, 2)。我们发现5 > 2 * 2。对一个6×6的矩阵而言,最后一圈有四个数字,对应的坐标仍然为(2, 2)。我们发现6 > 2 * 2依然成立。于是我们可以得出,让循环继续的条件是columns > startX * 2 && rows > startY * 2。有了这些分析,我们就可以写出如下的代码:

void PrintMatrixClockwisely(int** numbers, int columns, int rows)

{

        if(numbers == NULL || columns <= 0 || rows <= 0)

                return;

 

        int startX = 0;

        int startY = 0;

 

        while(columns > startX * 2 && rows > startY * 2)

        {

                PrintMatrixInCircle(numbers, columns, rows, startX, startY);

 

                ++startX;

                ++startY;

        }

}

接下来我们分析如何在PrintMatrixInCircle中按照顺时针的顺序打印一圈的数字。如同在图中标注的那样,我们可以分四步来打印:第一步是从左到右打印一行(上图中黄色区域),第二步是从上到下打印一列(上图中绿色区域),第三步从右到左打印一行(上图中蓝色区域),最后一步是从下到上打印一列(上图中紫色区域)。也就是我们把打印一圈数字这个问题,分解成四个子问题。我们可以为每个子问题定义一个函数。四个步骤对应的函数名称我们分别定义为:PrintARowIncreasinglyPrintAColumnIncreasinglyPrintARowDecreasinglyPrintAColumnDecreasingly

现在我们暂时不考虑如何去实现这四个函数,而是先考虑我们需要分别给这些函数传入哪些参数。第一步打印一行时,所有的数字的行号是固定的(startY),不同数字的列号不同。我们需要传入一个起始列号(startX)和终止列号(endX)。第二步打印一列时,所有的数字的列号是固定的,不同的数字的行号不同。我们需要传入一个起始行号(startY + 1)和一个终止行号(endY)。第三步和第四步和前面两步类似,读者可以自己分析。

接下来我们需要考虑特殊情况。并不是所有数字圈都需要四步来打印。比如当一圈退化成一行的时候,也就是startY等于endY的时候,我们只需要第一步就把所有的数字都打印完了,其余的步骤都是多余的。因此我们需要考虑第二、三、四步打印的条件。根据前面我们分析,不难发现打印第二步的条件是startY < endY。对于第三步而言,如果startX等于endX,也就是这一圈中只有一列数字,那么所有的数字都在第二步打印完了;如果startY等于endY,也就是这一圈中只有一行数字,那么所有的数字都在第一步打印完了。因此需要打印第三步的条件是startX < endX && startX < endY。第四步最复杂,首先startX要小于endX,不然所有的数字都在一列,在第二步中就都打印完了。另外,这个圈中至少要有三行数字。如果只有一行数字,所有数字在第一步中打印完了;如果只有两行数字,所有数字在第一步和第三步也都打印完了。因此打印第四步需要的条件是startY < endY – 1

有了前面的分析,我们就能写出PrintMatrixInCircle的完整代码如下:

void PrintMatrixInCircle(int** numbers, int columns, int rows,

                                  int startX, int startY)

{

        int endX = columns - 1 - startX;   //通过画图可以观察出来i。

        int endY = rows - 1 - startY;

 

        PrintARowIncreasingly(numbers, columns, rows, startY, startX, endX);

 

        if(startY < endY)

                PrintAColumnIncreasingly(numbers, columns, rows, endX, startY + 1, endY);

 

        if(startX < endX && startY < endY)

                PrintARowDecreasingly(numbers, columns, rows, endY, endX - 1, startX);

 

        if(startX < endX && startY < endY - 1)

                PrintAColumnDecreasingly(numbers, columns, rows, startX, endY - 1, startY + 1);

}

接下来我们考虑如何打印一行或者一列。这对我们来说不是一件很难的事情。以函数PrintARowIncreasingly为例,我们只需要一个循环,把行号为startY,列号从startXendX的所有数字依次从数组中取出来并逐个打印就行了,对应的代码是:

void PrintARowIncreasingly(int** numbers, int columns, int rows,

                                int y, int firstX, int lastX)

{

        for(int i = firstX; i <= lastX; ++i)

        {

                int number = *(*(numbers + y) + i);

        printf("%d\t", number);

        }

}

剩下的三个函数与此类似,代码依次如下:

void PrintAColumnIncreasingly(int** numbers, int columns, int rows,

                                  int x, int firstY, int lastY)

{

        for(int i = firstY; i <= lastY; ++i)

        {

                int number = *(*(numbers + i) + x);

        printf("%d\t", number);

        }

}

void PrintARowDecreasingly(int** numbers, int columns, int rows,

                                int y, int firstX, int lastX)

{

        for(int i = firstX; i >= lastX; --i)

        {

                int number = *(*(numbers + y) + i);

        printf("%d\t", number);

        }

}

void PrintAColumnDecreasingly(int** numbers, int columns, int rows,

                                  int x, int firstY, int lastY)

{

        for(int i = firstY; i >= lastY; --i)

        {

                int number = *(*(numbers + i) + x);

        printf("%d\t", number);

        }

}

 

 

-----------------------------------------------------------------------------------------------

你可能感兴趣的:(算法)