《剑指offer》面试题29:顺时针打印矩阵

更多剑指offer面试习题请点击:《剑指offer》(第二版)题集目录索引

题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。例如,如果输入如下矩阵:

/*
1   2   3   4
5   6   7   8
9   10  11  12
13  14  15  16
*/

则依次打印出数字 1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10。

解题思路:
    这题我们可以把一个矩阵想象成若干个圈套在一起组成。我们可以用一个循环来打印矩阵,每次打印矩阵中的一个圈。

    下面分析循环结束条件。假设这个矩阵行数是rows,列数是columns。打印第一圈的第一个数的坐标是(0, 0),第二圈的第一个数的坐标是(1, 1),所以我们可以推出,每圈第一个数的行坐标和列坐标是相同的。

    对于一个5×5的矩阵而言,最后一圈只有一个数,它的坐标是(2, 2),并且5>2×2。对于一个6×6的矩阵而言,最后一圈只有4个数,第一个数字的坐标是(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 start = 0;

    while (columns > start * 2 && rows > start * 2)
    {
        PrintMatrixInCircle(numbers, columns, rows, start);

        start++;
    }
}

    我们已经把循环打印矩阵的程序写好了,接下来就是要写打印一圈矩阵的程序。我们把打印一圈矩阵分为四步:第一步,从左到右打印一行;第二步,从上到下打印一列;第三步,从右到左打印一行;第四步,从下到上打印一列。每一步我们根据起始坐标和终止坐标设计一个循环就能打印一行或一列。

    但要注意,有可能最后一圈只有一行、只有一列或者只有一个数,那打印一圈就不需要四步了。接下来我们要分析在什么情况下打印一圈需要四步;在什么情况下打印一圈需要三步;在什么情况下打印一圈需要两步;在什么情况下打印一圈只需要一步;

  1. 只要进入了while开始打印,那第一步就是必备的,这点无可争议。如果这一圈只有一行 那就不需要第二步了。
  2. 当这一圈有两行或两行以上,那就需要第二步。即终止行号大于起始行号。
  3. 当这一圈有两行两列的时候,那就需要第三步。即终止行号大于起始行号且终止列号大于起始列号。
  4. 最后一圈有三行两列是就需要第四步。即终止行号比起始行号至少大2且终止列号大于起始列号。

        通过上面的分析我们可以写出如下代码:

void PrintMatrixInCircle(int** numbers, int columns, int rows, int start)
{
    int endX = columns - 1 - start; //终止列号
    int endY = rows - 1 - start;    //终止行号

    /*第一步:打印从左到右这一行*/
    for (int i = start; i <= endX; i++)
    {
        /*行号不变,列号增加*/
        int number = numbers[start][i]; 
        printNumber(number);
    }

    /*第二步:打印从上到下这一列,终止行号大于起始行号*/
    if (start < endY)
    {
        for (int i = start + 1; i <= endY; i++)
        {
            /*行号增加,列号不变*/
            int number = numbers[i][endX];
            printNumber(number);
        }
    }

    /*第三步:打印从右到左这一行,终止行号大于起始行号且终止列号大于起始列号。*/
    if (start < endX &&start < endY)
    {
        for (int i = endX - 1; i >= start; i--)
        {
            /*行号不变,列号减少*/
            int number = numbers[endY][i];
            printNumber(number);
        }
    }

    /*第四步:打印从下到上这一列,终止行号比起始行号至少大2且终止列号大于起始列号。*/
    if (start < endX && start < endY - 1)
    {
        for (int i = endY - 1; i >= start + 1; i--)
        {
            /*行号减少,列号不变*/
            int number = numbers[i][start];
            printNumber(number);
        }
    }
}

    最后完善打印单个数字代码。

void printNumber(int number)
{
    printf("%d\t", number);
}

测试代码:

void Test(int columns, int rows)
{
    printf("Test Begin: %d columns, %d rows.\n", columns, rows);

    if(columns < 1 || rows < 1)
        return;

    int** numbers = new int*[rows];
    for(int i = 0; i < rows; ++i)
    {
        numbers[i] = new int[columns];
        for(int j = 0; j < columns; ++j)
        {
            numbers[i][j] = i * columns + j + 1;
        }
    }

    PrintMatrixClockwisely(numbers, columns, rows);
    printf("\n");

    for(int i = 0; i < rows; ++i)
        delete[] (int*)numbers[i];

    delete[] numbers;
}

int main(int argc, char* argv[])
{
    /*
    1    
    */
    Test(1, 1);

    /*
    1    2
    3    4
    */
    Test(2, 2);

    /*
    1    2    3    4
    5    6    7    8
    9    10   11   12
    13   14   15   16
    */
    Test(4, 4);

    /*
    1    2    3    4    5
    6    7    8    9    10
    11   12   13   14   15
    16   17   18   19   20
    21   22   23   24   25
    */
    Test(5, 5);

    /*
    1
    2
    3
    4
    5
    */
    Test(1, 5);

    /*
    1    2
    3    4
    5    6
    7    8
    9    10
    */
    Test(2, 5);

    /*
    1    2    3
    4    5    6
    7    8    9
    10   11   12
    13   14   15
    */
    Test(3, 5);

    /*
    1    2    3    4
    5    6    7    8
    9    10   11   12
    13   14   15   16
    17   18   19   20
    */
    Test(4, 5);

    /*
    1    2    3    4    5
    */
    Test(5, 1);

    /*
    1    2    3    4    5
    6    7    8    9    10
    */
    Test(5, 2);

    /*
    1    2    3    4    5
    6    7    8    9    10
    11   12   13   14    15
    */
    Test(5, 3);

    /*
    1    2    3    4    5
    6    7    8    9    10
    11   12   13   14   15
    16   17   18   19   20
    */
    Test(5, 4);

    system("pause");
    return 0;
}

运行结果:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值