题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵:
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.
题目思路
当我们遇到一个复杂问题的时候,可以用图形来帮助我们思考。由于是以从外圈到内圈的顺序依次打印,我们可以把矩阵想象成若干个圈,如下图1(插图来着《剑指offer》)所示,我们可以用一个循环来打印矩阵,每一次打印矩阵中一个圈。
图1
接下来分析循环结束的条件。假设矩阵的行数为rows,列数为columns。打印第一圈的左上角的坐标是(0,0),第二圈的左上角的坐标是(1,1),以此类推。我们注意到,左上角的坐标中行标和列标总是相同的,于是可以在矩阵中选取左上角(start,start)的一圈作为我们的分析目标。
对一个5*5的矩阵,最后一圈只有一个数字,对应坐标为(2,2)。我们发现5>2*2。对于一个6*6的矩阵,最后一圈有4个数字,其左上角的坐标仍为(2,2)。我们分析6>2*2仍成立。归纳演绎我们可以得出,循环继续的条件是columns>startX*2并且rows>startY*2。
接着我们考虑如何打印一圈的功能,如图1所示,我们可以把打印一圈分四步:第一步从左到右打印一行,第二步从上到下打印一列,第三步从右到左打印一行,第四步从下到上打印一列。每一步我们根据起始坐标和终止坐标用一个循环就能打印出一行或者一列。
不过值得注意的是,最后一圈可能退化成只有一行、一列甚至一个数字,打印这样的一圈就不需要四步。图2(来着《剑指offer》)是几个退化的例子。
、
图2
因此我们要仔细分析打印时的每一步的前提条件。第一步总是需要的,因为打印一圈至少要有一步。如果只有一行就不要第二步了,也就是需要第二步的前提条件是终止行号大于起始行号。需要第三步的条件是至少有两行两列,也就是除了要求终止行号大于起始行号之外,还要满足终止列号大于起始列号。第四步的条件是至少有三行两列,也就是要求终止行号比起始行号至少大2,同时终止列号大于起始行号。
Java实现
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printMatrix(int [][] matrix) {
if(matrix==null)
return null;
int columns=matrix[0].length;
int rows=matrix.length;
int start=0;
ArrayList<Integer> list=new ArrayList<Integer>();
while(columns>start*2&&rows>start*2){
//表示最后一列
int endX=columns-1-start;
//表示最后一行
int endY=rows-1-start;
//从左到右输出一行
for(int i=start; i<=endX; i++){
int number=matrix[start][i];
list.add(number);
}
//从上到下输出一列
if(endY>start){
for(int i=start+1;i<=endY;i++){
int number=matrix[i][endX];
list.add(number);
}
}
//从右到左输出一行
if(endX>start&&endY>start){
for(int i=endX-1;i>=start;i--){
int number=matrix[endY][i];
list.add(number);
}
}
//从下到上输出一列
if(endY-start>=2&&endX>start){
for(int i=endY-1;i>start;i--){
int number=matrix[i][start];
list.add(number);
}
}
start++;
}
return list;
}
}