# 面试题 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
,循环结束,打印完成。