题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 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.


思路:显然是脑子里想的很简单,但是实际代码中的边界条件不太容易找准。因此主要分析边界。
如果是4×4的矩阵,顺时针打印第一行时什么时候作为结束的判断标志,顺时针从列开始时,什么时候开始,什么时候结束,都需要考虑清楚。显然本份代码使用的边界是这样的,一共分为4轮完成一圈的遍历,
1   2   3    4
5   6   7    8
9  10  11  12
13 14  15  16
  首先是123,然后是4,8,12。然后是16,15,14。最后13,9,5。由于这样的遍历方式,导致行在遍历或者列在遍历时,最后一个元素都无法打印,而是在下一种遍历方式中遍历。比如4是在第4列遍历时才打印,而不是在第一行的时候打印。因此这种遍历方式,需要考虑二种特殊情况,只有一行时以及只有一列时需要单独考虑,否则会漏掉最后一个元素,比如单行只有1234时,遍历只有123而不会出现4,因此单独考虑(列同理)。
  这种遍历方法在遍历到最内圈时,是(6),(7),(11),(10)的方式,也就是说,即使6和7在同一行,6是在行遍历时打印,7是作为列的首元素遍历时打印,而不是在6的行遍历时出现。
  这样遍历的优势是,遍历比较有规律,在任何一行遍历或者列遍历时,都是有头没尾。因此在边界条件的写法上,也比较有规律,比较容易找到边界比如都是以判断是否到了尾部,到尾部就停止循环,而将最后元素留给下一次循环。

import java.util.ArrayList;
public class Solution {
    ArrayList arr =new ArrayList();   
    public ArrayList printMatrix(int [][] matrix) {
       int startRow=0; //起始的行,上边界
       int startCow=0;  //起始列 左边界
       int row = matrix.length-1;  //二维数组行的长度  下边界
       int col = matrix[0].length-1;  //二维数组列的长度   右边界
        while(startRow <= row && startCow <= col){
               //打印一圈后,二维数组长度减2 宽度减2
            //也就是左边界+1,右边界-1,上边界+1,下边界-1
           printEdge(matrix,startRow++,startCow++,row--,col--);
        }
      return arr;
    }
  public void printEdge(int[][] matrix,int startRow,int startCow,int row,int col)
    {

        if(startRow == row){  //先判断是否只是一横行(上边界和下边界重合时只有一行)
            for(int i = startCow;i <= col;i++){
                arr.add(matrix[startRow][i]);
            }
        }
        else if(startCow == col){//判断是否只是一列(左边界和右边界重合时只有一列)
            for(int i=startRow;i<=row;i++){
                arr.add(matrix[i][startCow]);
            }
        }
        else {  //进入这里说明不止一行一列
            int curC = startCow;//用2个变量储存 记录当前位置  标记到哪一列了
            int curR = startRow; //标记到哪一行了。
            while(curC != col){    //行的遍历 (首个元素到倒数第二个元素打印)
                arr.add(matrix[startRow][curC]);
            curC++;//行坐标++  指针指向最后一个数,但并未添加到arr中。
            //指向的最后一个数,作为下一次遍历的首元素
            }
            while(curR!=row){//列的从上至下的遍历(打印首个元素到倒数第二个元素) 
                arr.add(matrix[curR][col]);
                curR++; //列坐标++,此时到指向列最后一个数,而并未添加到arr中
            }
            while(curC != startCow){   //行的从右向左
                arr.add(matrix[row][curC]);
                curC--; //最后一个元素留出来
            }
            while(curR != startRow){  //列的从下向上
                arr.add(matrix[curR][startCow]);
                curR--;//最后一个元素留出来,其实这个元素在行的从左到右遍历中已经打印
            }
        }
    }
}

也可以只使用一个变量。这是因为每一次行或者列遍历,只有一个角标在变。但是边界判断不如上面的简洁。某位大佬的写法:链接:https://www.nowcoder.com/questionTerminal/9b4c81a02cd34f76be2659fa0d54342a?f=discussion

import java.util.ArrayList;
public class Solution {
    ArrayList<Integer> list = new ArrayList<>();    
    public ArrayList<Integer> printMatrix(int [][] matrix) {
        int rows = matrix.length;
        int columns = matrix[0].length;
        int start = 0;
        while(rows > start*2 && columns > start*2){
            printMatrix(matrix, rows, columns, start);
            start++;
        }
        return list;
    }    

    public void printMatrix(int[][] matrix, int rows, int columns, int start) {
        // 从左到右打印一行
        for(int i = start; i < columns - start; i++)
            list.add(matrix[start][i]);
        // 从上到下打印一列
        for(int j = start + 1; j < rows - start; j++)
            list.add(matrix[j][columns - start - 1]);
        // 从右到左打印一行
        for(int m=columns-start-2; m>=start && rows-start-1 > start; m--)
            list.add(matrix[rows - start - 1][m]);
        // 从下到上打印一列
        for(int n=rows-start-2; n>=start+1 && columns-start-1 > start; n--)
            list.add(matrix[n][start]);
    }
}