题目描述:
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
例如: 1 2 3 4
5 6 7 8 输出为: 1 ,2 ,3 ,4 ,8 ,12 ,11 ,10 ,9 ,5 ,6 ,7
9 10 11 12
刚看题目觉得很容易理解,感觉不涉及到什么算法,只要遍历时确定好边界值,就可以了,但一时间又不知道怎么确定,就先按其它思路。
我先再申请一个和该矩阵大小相同的int型二维数组,然后只要走过的地方就设置为1,然后设置四个方向,右,下,左,上;这四个方向是有各自顺序的,只要当前方向上可以走,就走到尽头,不然就往下一个方向跳,四个方向都不可走了就说明结束。代码如下:
public ArrayList<Integer> printMatrix(int[][] matrix) {
//错误输入校验
if (matrix == null) return null;
ArrayList resList = new ArrayList();
// 右 下 左 上
int[][] dir = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
int[][] map = new int[matrix.length][matrix[0].length];
//用于标记是否结束
boolean isEnd = false;
//起始位置是0行0列
int i = 0, j = 0;
//初始位置往右
int currDir = 0;
while (true) {
//将当前点加入list中
resList.add(matrix[i][j]);
//在map上标记其是被走过的
map[i][j] = 1;
//查看路上是否可走
int count = 0;
while (true) {
count++;
//防止上下越界 左右越界
if (i + dir[currDir][0] < map.length && i + dir[currDir][0] >= 0 && j + dir[currDir][1] < map[0].length && j + dir[currDir][1] >= 0) {
//找到可以走的位置
if (map[i + dir[currDir][0]][j + dir[currDir][1]] != 1) break;
}
if (count > 4) {
isEnd = true;
break; //已经把四个方向找遍了都不行
}
currDir += 1;
currDir %= 4;
}
//判断是否四个方向都判断了
if (isEnd) break;
//往对应方向进行移动
i += dir[currDir][0];
j += dir[currDir][1];
}
return resList;
}
这样虽然可以解决问题,但是无故的多开销了一些内存空间来存放一个判断该点是否走过的数组,而且该数组大小和矩阵的大小一样,很浪费空间,看书上通过观察它的规律,可以得到更简便的方法:
public ArrayList<Integer> printMatrix(int[][] matrix) {
//错误校验
if (matrix == null) return null;
ArrayList resList = new ArrayList();
//记录该矩阵的行列
int rows = matrix.length,columns = matrix[0].length;
int start = 0;
while (columns > start * 2 && rows > start*2) {
//endX表示列的终点坐标,因为每一次打印最外一圈,上下和两边的行列就会减一
int endX = columns - 1 - start;
//endY表示行坐标的终点
int endY = rows - 1 - start;
//从左到右打印一行
for (int i = start; i <= endX; i++) {
resList.add(matrix[start][i]);
}
//从上到下打印一列
if (start < endY) {
for (int i = start + 1; i <= endY; i++) {
resList.add(matrix[i][endX]);
}
}
//从右往左打印一行
if (start < endX && start < endY) {
for (int i = endX - 1; i >= start; i--) {
resList.add(matrix[endY][i]);
}
}
//从下到上打印一列
if (start < endX && start < endY - 1) {
for (int i = endY - 1; i >= start + 1; i--) {
resList.add(matrix[i][start]);
}
}
start++;
}
return resList;
}
这样就可以不用其它多余的消耗而解决问题。
在网上还看到一种很好的思路,可以每把这个矩阵第一行输出后,然后好像魔方一样扭转一次,把右边的边挪到上面,这样只需要不停的输出上面的边即可,
例如
1 2 3 6 9
4 5 6 ---输出第一行为 1, 2 3 后进行翻转变为--- 5 8
7 8 9 4 7
代码如下:
public ArrayList<Integer> printMatrix(int[][] matrix) {
if (matrix == null) return null;
ArrayList<Integer> resList = new ArrayList<>();
int row = matrix.length;
while (row != 0) {
//输出第一行
for (int i = 0; i < matrix[0].length; i++) {
resList.add(matrix[0][i]);
}
//如果该矩阵只有一行的话就结束
if (row == 1)
break;
//对矩阵进行翻转
matrix = turn(matrix);
//因为翻转会导致行列互换,所以要重新得到行数
row = matrix.length;
}
return resList;
}
//将矩阵除第一行外的列做左旋转
public int[][] turn(int[][] matrix) {
int col = matrix[0].length;
int row = matrix.length;
int[][] newMatrix = new int[col][row - 1];
for (int j = col - 1; j >= 0; j--) {
for (int i = 1; i < row; i++) {
newMatrix[col - 1 - j][i - 1] = matrix[i][j];
}
}
return newMatrix;
}
不过这样子每次输出都要做一次旋转,要重新得到一个新的矩阵,还是没有直接通过数学归纳总结规律来得快,所以还是第二种方法好,但思路很好,值得学习。