剑指offer-面试题 20:顺时针打印矩阵

来源:互联网 发布:安全数据库 编辑:程序博客网 时间:2024/06/06 00:30

# 面试题 20:顺时针打印矩阵
题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。


1. 思路

  题目理解很简单。重要的是如何建模呢?容易想到,要做到从外往里打印,每当执行一次打印操作,必须将某个参数减1(我们先不用考虑具体的代码细节,只需要有这个思路即可)。对于具体的参数设计,我们在后面会有详细的解释。我们通过对题目分析,除了代码的实现,得出了两个比较重要的问题:
  (1)代码如何终止?
  (2)而对于极端情况(例如只有1行或者1列,或者只有一个数)该怎么做?
以下是我的详细思路:

1.1 建模


打印矩阵

  如图是一个二维矩阵的简单示意图。如果我们要顺时针打印一个矩阵,那么通常会分成四部分,先左右横,后上下竖,后右左横,最后下上竖,一个圈完成,之后类推。
实现顺时针转动的话,每部分完成后需要将某个参数减1(我们先不去关注具体是哪个参数),之后执行下一部分。
根据上述思路,我设置了四个轴,分别为:up轴right轴down轴left轴
完成一圈的过程是:
(1)左右横,up轴 向下移动一行;
(2)上下竖,right轴 向左移动一行;
(3)右左横,down轴 向上移动一行;
(4)下上竖,left轴 向右一行。
之后up轴从第二行开始(假设初始值是第1行)从左往右遍历,right轴从倒数第二列开始向下遍历(初始值最后一列),down轴从倒数第二行开始从右往左遍历(初始值为倒数第一行),left轴从第二列从下往上遍历(初始值为第一列)……

1.2 对结束条件的判断

那么如何判断执行到头了呢?我们可以从简单的情况开始思考:
(1)当二维数组只有一个数字时,行和列数均为1,那么只要打印出这个数就可以了。此时四个轴的值是什么呢?up,down,right和left均为1;
(2)当二维数组只有一行时,up = down ,left = 1,right = 列数(这里是列数)。
打印操作执行为打印这一行。用代码实现就是:

    for(int i = 0; i < rowLen - 1; i++) {  //列数不为1,逐个打印        System.out.println(a[0][i] + " ");  //只有一行    }

(3)当二维数组只有一列时,left = right ,up = 1,down = 行数(这里是行数)。
打印操作执行为打印这一列,代码跟上面类似。
(4)更复杂的情形:
多行多列时,我们发现,打印的操作都是一个for循环,那么怎么确定到底是打印数组的哪条边呢?
  我的想法是:打印有四个方向,每次从左往右横向打印完了,之后会打印从上往下。下次横向再打印时,方向肯定是从右往左;
  每次从上往下纵向打印完了,之后横向从左往右打印。下次纵向再打印时,方向总是从下往上。
  所以想法是设定两个方向判断变量rDir和cDir:其中rDir为行的方向变量,为1时从左往右,为0从右往左打印;cDir为列的方向变量,为1从上往下,为0从下往上打印。在代码中会对这两个变量进行赋值,用它们来判断打印的方向,下面是一个简单的情形:

    int rDir = 1;  // 行的方向变量,为1从左往右,为0从右往左打印    int cDir = 1;  // 列的方向变量,为1从上往下,为0从下往上打印    ……    if(rDir == 1) {        printLR(a, left, right, up, down);        up++;        rDir = 0;    }else {        printRL(a, left, right, up, down);        down--;        rDir = 1;    }       ……    if(up <= down) {        if(cDir == 1) {            printUD(a, left, right, up, down);            right--;            cDir = 0;        }else {            printDU(a, left, right, up, down);            left++;            cDir = 1;                   }   

rDir默认为1,也就是从左到右打印,打印完后rDir赋值为0;为0时,代表从右到左打印,打印完后rDir变成0。
cDir默认为1,也就是从下到上打印,打印完后cDir赋值为0;为0时,代表从上到下打印,打印完后cDir变成0。
其中四个print方法是封装for循环的按方向打印的函数,具体实现会在后续代码中给出。
以上是我们对打印方向的确定和简单实现,那么回到我们最初提出的问题:
如何判断结束呢?其实就是判断up和down,left和right的大小。这部分比较简单,我们直接看代码:


2. 代码

package swordOffer;/**面试题 20:顺时针打印矩阵 * 题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。 *  * @author Stephen Huge * @date 17/04/22 */public class Ex20PrintMatrixClockwisely {    public static void main(String[] args) {//      int[][] array = new int[3][0];//      int[][] array = {{1,2,3,4,5}};//      int[][] array = {{1},{2},{3},{4},{5}};//      int[][] array = {{1,2,3},{2,3,4}};              int[][] array = {{1,2},{2,3},{3,4},{4,5},{5,6}};        //      int[][] array = {{1,2,3},{2,3,4},{3,4,5}};//      int[][] array={{1,2,3,4,5},{5,6,7,8,9},{9,10,11,12,13},{13,14,15,16,17},{17,18,19,20,21},{21,22,23,24,25}};        Ex20PrintMatrixClockwisely pmcw = new Ex20PrintMatrixClockwisely();        pmcw.printMatrixClociwisely(array);    }    public void printMatrixClociwisely(int[][] a) {        if(a == null || a.length == 0 || (a.length > 0 && a[0].length == 0)) {            System.out.println("Length error!");            return;        }        int up = 0;        int down = a.length - 1;    //每列的行数        int left = 0;        int right = a[0].length - 1;//每行的列数        int rDir = 1;   // 行的方向变量,为1从左往右,为0从右往左打印        int cDir = 1;   // 列的方向变量,为1从上往下,为0从下往上打印        while(true) {            if(left <= right) {                if(rDir == 1) {                    printLR(a, left, right, up, down);                    up++;                    rDir = 0;                }else {                    printRL(a, left, right, up, down);                    down--;                    rDir = 1;                }               }else {                System.out.println("left > right");                return;            }            if(up <= down) {                if(cDir == 1) {                    printUD(a, left, right, up, down);                    right--;                    cDir = 0;                }else {                    printDU(a, left, right, up, down);                    left++;                    cDir = 1;                           }               }else {                System.out.println("up < down");                return;            }           }    }    private void printLR(int[][] a, int left, int right, int up, int down) {    //从左到右打印        for(int i = left; i <= right; i++) {            System.out.print(a[up][i] + " ");        }        System.out.print(". ");    }    private void printRL(int[][] a, int left, int right, int up, int down) {    //从右到左打印        for(int i = right; i >= left; i--) {            System.out.print(a[down][i] + " ");        }        System.out.print(". ");    }    private void printUD(int[][] a, int left, int right, int up, int down) {    //从上到下打印        for(int i = up; i <= down; i++) {            System.out.print(a[i][right] + " ");        }        System.out.print(". ");    }    private void printDU(int[][] a, int left, int right, int up, int down) {    //从下到上打印        for(int l = down;l >= up; l--) {        System.out.print(a[l][left] + " ");    }        System.out.print(". ");    }}

运行结果:

1 2 . 3 4 5 6 . 5 . 4 3 2 . 

在一个无限循环中,不断的判断up和down,left和right的大小。如果up > down 或者left > right ,循环结束,打印完成。

0 0
原创粉丝点击