剑指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.

分析

这道题思路很简单,但是实现真的是比较繁琐,而且需要考虑到几种特殊情况,比如不是方阵的各种情况。这就需要在纸上画出各种情况,然后总结规律。

初次看到这道题的时候,感觉千头万绪,不知道从何下手。这个时候,你需要告诉自己:一定要静下心来,总会找出规律的,不要一开始就想代码实现

然后找到了如下规律:

因为打印只有四种方式:从左到右,从右到左,从上到下,从下到上,所以为了逻辑清晰一点,定义了四种不同的函数来分别实现以上四种功能。易知,这需要一个循环来实现,但是终止条件是什么呢?我是这样实现的:声明两个变量rowSpan,colSpan。终止条件为 rowSpan <= 0 && colSpan <= 0。

每遍历一行,行距减1,遍历一列列距减1.

然后需要根据每次不同的遍历情况更改row和col变量的起点和界限,故MaxCol,MaxRow需要设定,每次从上到下打印一列,MaxCol减1,从右到左打印一行,MaxRow减1.

以上就是主要的思路。

但是还有很多需要注意的细节地方:

比如在有时候row可能大于0,而rowSpan可能小于0,按理应该终止,但是如果根据它们之和判定,可能由于它们的和大于0,而遍历一次。所以我再每次if判断之后对rowSpan和colSpan判断一次,如果小于0,则break,终止。

代码实现

    package com.study;

/**
 * 打印螺旋矩阵
 */
public class suanfa12 {
    public static void printSpiralMatrix(int[][] arr) {
        if (arr == null)
            return;
        int row = 0;
        int col = 0;
        // 终止条件
        int MaxRow = arr.length - 1;
        int MaxCol = arr[0].length - 1;

        int rowSpan = MaxRow;
        int colSpan = MaxCol;

        while (rowSpan > 0 || colSpan > 0) {
            if (col + colSpan <= MaxCol) {
                if (colSpan < 0)
                    break;
                printRow_LeftToRight(arr, col, col + colSpan, row);
                col += colSpan;
                rowSpan--;// 只要遍历一行,行距必然会少一行
                row++; // 从左到右遍历完一行,行号加一
            }

            if (row + rowSpan <= MaxRow) {
                if (rowSpan < 0)
                    break;
                printCol_UpToDown(arr, row, row + rowSpan, col);
                colSpan--;// 遍历一列,列距会少一列
                col--; // 从上到下遍历完一列,列号减一
                MaxCol--;
                row += rowSpan;
            }

            if (col - colSpan >= 0) {
                if (colSpan < 0)
                    break;
                printRow_RightToLeft(arr, col, col - colSpan, row);
                rowSpan--;// 只要遍历一行,行距必然会少一行
                row--; // 从右到左遍历完一行,行号减一
                MaxRow--;
                col -= colSpan;
            }

            if (row - rowSpan >= 0) {
                if (rowSpan < 0)
                    break;
                printCol_DownToUp(arr, row, row - rowSpan, col);
                colSpan--;// 遍历一列,列距会少一列
                col++;// 从下到上遍历完一列,列号加一
                row -= rowSpan;
            }
        }

    }

    /* 定义了四种打印函数 */
    public static void printRow_LeftToRight(int[][] a, int start, int end,
            int rows) {
        for (int i = start; i <= end; i++) {
            System.out.print(a[rows][i] + " ");
        }
        System.out.println();
    }

    public static void printRow_RightToLeft(int[][] a, int end, int start,
            int rows) {
        for (int i = end; i >= start; i--) {
            System.out.print(a[rows][i] + " ");
        }
        System.out.println();
    }

    public static void printCol_UpToDown(int[][] a, int start, int end, int cols) {
        for (int i = start; i <= end; i++) {
            System.out.print(a[i][cols] + " ");
        }
        System.out.println();
    }

    public static void printCol_DownToUp(int[][] a, int end, int start, int cols) {
        for (int i = end; i >= start; i--) {
            System.out.print(a[i][cols] + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int[][] arr3 = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 },
                { 13, 14, 15, 16 } };// 定义一个二维数组
        int[][] arr1 = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };// 定义一个二维数组
        int[][] arr2 = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 } };// 定义一个二维数组
        int[][] arr4 = { { 1, 2, 3, 4 } };
        int[][] arr = { { 1 }, { 2 }, { 3 }, { 4 } };
        printSpiralMatrix(arr);
    }
}

备注

在主函数中设定了四种可能出现的情况,都通过测试。

总结

这道题的实现与《剑指offer》上的不太一样,感觉书上的思路利用了一个数学定理,但是我不太理解这个定理。所以,没有采用它的方法,而是用我自己的方法实现了。

虽然忙活了一下午,但是还是弄出来了,一句话,自己做的饭最香。记得第一次看到这个题的时候,根本不知道从何下手,也不认为自己会将之实现。但是这次竟然独立完成了,给自己点个赞。

感想:一定不要放弃,在不知道从何下手时,不妨跳出来看看,等你看出来它的规律的时候,实现起来,就会相对容易一点。