题目描述
思路
由题目可以看出,每次从一个已经腐烂的橘子向外层蔓延,不难看出这是在模拟广度优先搜索BFS的例程
因此我们所用到的就是BFS算法
大致过程如下:
- 首先遍历整个网格,找到已经腐烂的橘子,加入队列;
- 从已经腐烂的橘子去更新相邻的橘子,直到队列为空;
- 若队列为空,网格上还有新鲜橘子,返回-1,否则返回总时间
问题
但在实际编程中,却遇到了两个问题
1.入队数据
入队的数据应该是此时橘子的坐标,那么应该是什么数据类型?
2.烂橘子的扩散
如何去判断当前橘子的四个正方向坐标,数组的边界判定如何实现?
解决
1.
入队的数据的话,采取的方案就是计算当前坐标的代数值,如一个4*4的网格,左上角作为坐标原点,那么(2,2)的坐标代数值为2 *4 + 2 = 10
推广为一般化形式:
一个M x N的矩阵,(i, j)的坐标代数值= i * N + j
2.
关于四个正方向的判定问题采取的是官方的解答,即创建两个数组
int[] dr = new int[]{-1, 0, 1, 0};
int[] dc = new int[]{0, -1, 0, 1};
用一个4层循环 每次改变行/列的值
如果当前坐标(i,j)是合法的,并且该网格上的是新鲜的橘子,更新其值并入队列
for(int k=0; k<4; k++){
int ni = i + dr[k];
int nj = j + dc[k];
if(ni>=0 && ni<grid.length && nj>=0 && nj<grid[0].length && grid[ni][nj]==1){
grid[ni][nj] = 2;
queue.add(ni*grid[0].length+nj);
}
}
完整的代码
class Solution {
public int orangesRotting(int[][] grid) {
int[] dr = new int[]{-1, 0, 1, 0};
int[] dc = new int[]{0, -1, 0, 1};
int times = 0;
Queue<Integer> queue = new LinkedList<>();
//烂橘子入队
for(int i=0; i<grid.length; i++){
for(int j=0; j<grid[i].length; j++){
if(grid[i][j] == 2)
queue.add(i*grid[0].length+j);//将坐标值入队
}
}
while(!queue.isEmpty()){
int i, j, t;
int size = queue.size();
for(int s=0; s<size; s++){
t = queue.poll();
i = t / grid[0].length;
j = t % grid[0].length;
for(int k=0; k<4; k++){
int ni = i + dr[k];
int nj = j + dc[k];
if(ni>=0 && ni<grid.length && nj>=0 && nj<grid[0].length && grid[ni][nj]==1){
grid[ni][nj] = 2;
queue.add(ni*grid[0].length+nj);
}
}
}
if(!queue.isEmpty())
times++;
}
for(int i=0; i<grid.length; i++){
for(int j=0; j<grid[i].length; j++){
if(grid[i][j] == 1)
return -1;
}
}
return times;
}
}
扩展1:广度优先搜索 BFS
基本策略
越早被访问到的顶点,其邻居越优先被选用
不难发现,广度优先搜索即树结构中层序遍历,用到的辅助结构为——队列
以下是邓俊辉《数据结构》中的样例:
扩展2:深度优先搜索 DFS
1. 基本策略
优先选取最后一个被访问到的顶点的邻居
本质上来说就是递归+回溯
2. 注意事项
- 算法遍历边和访问顶点的顺序与图的表示方式有关,而不只与图的结构和算法有关
- DFS中,每条边都会被访问两次, 而且第二次为发现这个点已经被标记过了(已经访问过,不再重复访问)