前言
对于矩阵搜索问题,就像图的搜索一样,熟练掌握DFS、BFS是关键。
一、DFS
1、思想
通过DFS去寻找满足条件的格子,而已经访问过的格子就要标记一下。当遇到递归出口时,返回一次就+1。从{0,0}开始行走,走的地方只能是下面和右面。
2、源码
int[][] visited;
int maxi, maxj, k;
public int movingCount(int m, int n, int k) {
//设置每个递归函数要使用的公共参数
visited = new int[m][n];
maxi = m;
maxj = n;
this.k = k;
//调用递归求解
return DFS(0, 0);
}
//DFS+剪枝
public int DFS(int i, int j) {
//递归出口
if (i == maxi || j == maxj || visited[i][j] == 1 || sum(i) + sum(j) > k)
return 0;
visited[i][j] = 1;
//从第一个节点出发,向右走和向下走就可以遍历完所有可达节点
return DFS(i + 1, j) + DFS(i, j + 1) + 1;
}
//给定一个数字,在100之类,计算它所有位数之和
public int sum(int data) {
int sum = 0;
while (data > 0) {
sum += data % 10;
data /= 10;
}
return sum;
}
因为数据0<=data<=100,所以也可以写成
(data % 10 + data / 10)
二、BFS
通过辅助队列完成所有格子的遍历,加入队列中的格子要标记为已访问过。
1、源码
//BFS寻找矩阵格子数
public int movingCount(int m, int n, int k) {
//用队列完成BFS
int[][] visited = new int[m][n];
int result = 1;
List<Grid> queue = new LinkedList<>();
int i = 0, j = 0;
visited[i][j] = 1;
queue.add(new Grid(i, j));
Grid grid = null;
while (queue.size() != 0) {
grid = queue.remove(0);
i = grid.getI();
j = grid.getJ();
if (i < m - 1 && visited[i + 1][j] == 0 && sum(i + 1) + sum(j) <= k) {
queue.add(new Grid(i + 1, j));
visited[i + 1][j] = 1;
result++;
}
if (j < n - 1 && visited[i][j + 1] == 0 && sum(i) + sum(j + 1) <= k) {
queue.add(new Grid(i, j + 1));
visited[i][j + 1] = 1;
result++;
}
}
return result;
}
//给定一个数字,在100之类,计算它所有位数之和
public int sum(int data) {
int sum = 0;
while (data > 0) {
sum += data % 10;
data /= 10;
}
return sum;
}
class Grid {
int i;
int j;
public Grid(int i, int j) {
this.i = i;
this.j = j;
}
public int getI() {
return i;
}
public int getJ() {
return j;
}
}
注:上面写起来稍显复杂,第一,可以不用Grid这个类;第二,DFS种两个if显得太多,毕竟里面的操作是类似的。
2、改进源码BFS
//BFS寻找矩阵格子数
public int movingCount(int m, int n, int k) {
//用队列完成BFS
int[][] visited = new int[m][n];
Queue<int[]> queue = new LinkedList<>();
int i = 0, j = 0;
visited[i][j] = 1;
queue.offer(new int[]{i,j});
int result = 1;
int[] grid;
//设置辅助矩阵
int[][] E = new int[][]{{1,0},{0,1}};
while (!queue.isEmpty()) {
grid = queue.poll();
i = grid[0];
j = grid[1];
for(int count = 0;count < 2;count++){
int newi = i + E[0][count];
int newj = j + E[1][count];
if (newi >= m || newj >= n || visited[newi][newj] == 1 || newi % 10 + newi / 10 + newj % 10 + newj / 10 > k)
continue;
queue.offer(new int[]{newi, newj});
visited[newi][newj] = 1;
result++;
}
}
return result;
}
总结
1)时间复杂度,O(mn),mn为总格子数。
2)空间复杂度,O(mn),mn为visited矩阵的大小。递归栈的深度最大也为mn,当只有一列时。
注:掌握DFS、BFS是解决矩阵搜索、图搜索的关键基础。