矩阵的顺时针打印
- 前言
- 一、矩阵顺时针打印
- 1、题目
- 2、解答
- A.DFS
- B.模拟
- 总结
- 参考文献
前言
通过矩阵的顺时针打印来学习改进的DFS、和模拟的思想。
一、矩阵顺时针打印
1、题目
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
2、解答
A.DFS
按照自定义的顺序来DFS。
思想)开始向左递归,碰壁(越界或已访问)时,转向为向下;碰壁时,转向为向左;碰壁时,转向为向上。如此循环,直到四处碰壁,递归结束。
public class Imitate29 {
//offer 29 顺时针打印矩阵。
int count;//为res数组计数
int[] res;//需返回的数组
int[][] matrix;//传入矩阵
int[][] visited;//是否被访问,0为未访问,1为访问过。
public int[] spiralOrder(int[][] matrix) {
//一、总体思路:dfs先左,后下,再上,再右,访问过的打标记来剪枝就行(建立在dfs上,但是做一些符合题意的递归走向)
//1. 考虑空矩阵的情况
if (matrix.length == 0 || matrix[0].length == 0)
return new int[]{};
//2. 初始化
this.matrix = matrix;
res = new int[matrix.length * matrix[0].length];
visited = new int[matrix.length][matrix[0].length];
//3. 调用dfs,给数组res填正确的值
dfs(0, 0, 0);
//4. 返回结果res
return res;
}
public boolean dfs(int i, int j, int right) {
//越界则返回true,表示走不通
if (j == matrix[0].length || j == -1 || i == -1 || i == matrix.length)
return true;
//访问过就返回true,表示走不同
if (visited[i][j] == 1)
return true;
//满足两个条件,开始记录数值,并设置是否访问过。
res[count++] = matrix[i][j];
visited[i][j] = 1;
//right只的是一个方向,0:右;1:下;2:左;3:上。然后循环就可以把数据访问完。
if (right == 0) {
boolean flag = dfs(i, j + 1, right);
//碰壁(越界或碰到以访问过的)时,才能开始转向。
if (flag)
dfs(i + 1, j, (right + 1) % 4);
}
//根据每次方向,来递归访问。
if (right == 1) {
boolean flag = dfs(i + 1, j, right);
if (flag)
dfs(i, j - 1, (right + 1) % 4);
}
if (right == 2) {
boolean flag = dfs(i, j - 1, right);
if (flag)
dfs(i - 1, j, (right + 1) % 4);
}
if (right == 3) {
boolean flag = dfs(i - 1, j, right);
if (flag)
dfs(i, j + 1, (right + 1) % 4);
}
return false;
}
}
2)简化版
class Imitate {
//offer 29 顺时针打印矩阵。
int count;
int[] res;
int[][] matrix;
int[][] visited;
public int[] spiralOrder(int[][] matrix) {
//dfs先左,后下,再上,再右,访问过的打标记来剪枝就行
if (matrix.length == 0 || matrix[0].length == 0)
return new int[]{};
this.matrix = matrix;
res = new int[matrix.length * matrix[0].length];
visited = new int[matrix.length][matrix[0].length];
dfs(0, 0, 0);
return res;
}
public boolean dfs(int i, int j, int right) {
if (j == matrix[0].length || j == -1 || i == -1 || i == matrix.length)
return true;
if (visited[i][j] == 1)
return true;
res[count++] = matrix[i][j];
visited[i][j] = 1;
if (right == 0 && dfs(i, j + 1, right)) {
dfs(i + 1, j, (right + 1) % 4);
} else if (right == 1 && dfs(i + 1, j, right)) {
dfs(i, j - 1, (right + 1) % 4);
} else if (right == 2 && dfs(i, j - 1, right)) {
dfs(i - 1, j, (right + 1) % 4);
} else if (right == 3 && dfs(i - 1, j, right)) {
dfs(i, j + 1, (right + 1) % 4);
}
return false;
}
}
B.模拟
模拟顺时针的过程。
思想)通过4个指针,指向行的有效开始和有效结束,指向列的有效开始和有效结束。然后如此顺时针循环填值,直到res数组被填满值。
public int[] spiralOrder(int[][] matrix) {
//思路:模拟
//1. 考虑空矩阵的情况
if (matrix.length == 0 || matrix[0].length == 0)
return new int[]{};
//2. 初始化
int[] res = new int[matrix.length * matrix[0].length];
//3. 模拟
//定义上下边界
int i1, i2, j1, j2, count;
//初始化各边界
i1 = j1 = count = 0;
i2 = matrix.length - 1;
j2 = matrix[0].length - 1;
//正式模拟
while (count != res.length) {
for (int i = j1; i <= j2 && count != res.length; i++) {
res[count++] = matrix[i1][i];
}
i1++;
for (int i = i1; i <= i2 && count != res.length; i++) {
res[count++] = matrix[i][j2];
}
j2--;
for (int i = j2; i >= j1 && count != res.length; i--) {
res[count++] = matrix[i2][i];
}
i2--;
for (int i = i2; i >= i1 && count != res.length; i--) {
res[count++] = matrix[i][j1];
}
j1++;
}
//4. 返回结果res
return res;
}
总结
1)DFS的变形,掌握好DFS,在此基础上改进来完成自定义搜索的功能。
2)模拟,通过指针等附加手段来模拟实现。
3)Time:都是O(n),Space:都是O(n),但是模拟不需要递归栈,也不需要visited二维数组。