多源广度优先搜索
- 所有的腐烂橘子在广度优先搜索上是等价于同一层的节点的。
- 假设这些腐烂橘子刚开始是新鲜的,而有一个腐烂橘子(我们令其为超级源点)会在下一秒把这些橘子都变腐烂,而这个腐烂橘子刚开始在的时间是 −1,那么按照广度优先搜索的算法,下一分钟也就是第 0分钟的时候,这个腐烂橘子会把它们都变成腐烂橘子,然后继续向外拓展,所以其实这些腐烂橘子是同一层的节点。那么在广度优先搜索的时候,我们将这些腐烂橘子都放进队列里进行广度优先搜索即可,最后每个新鲜橘子被腐烂的最短时间 dis[x][y] 其实是以这个超级源点的腐烂橘子为起点的广度优先搜索得到的结果。
- 为了确认是否所有新鲜橘子都被腐烂,可以记录一个变量 cnt 表示当前网格中的新鲜橘子数,广度优先搜索的时候如果有新鲜橘子被腐烂,则 cnt=cnt−1 ,最后搜索结束时如果 cnt 大于 0,说明有新鲜橘子没被腐烂,返回 −1 ,否则返回所有新鲜橘子被腐烂的时间的最大值即可,也可以在广度优先搜索的过程中把已腐烂的新鲜橘子的值由 1 改为 2,最后看网格中是否有值为 1 即新鲜的橘子即可。
class Solution {
// 用于上下左右移动
int[] row = new int[]{0, 1, 0, -1};
int[] col = new int[]{1, 0, -1, 0};
public int orangesRotting(int[][] grid) {
int r = grid.length;
int c = grid[0].length;
// 先把所有烂了的橘子放入队列中
Queue<Integer> queue = new ArrayDeque<>();
Map<Integer, Integer> map = new HashMap<>(); // <橘子, 第几秒烂的>
for(int i=0; i<r; i++){
for(int j=0; j<c; j++){
if(grid[i][j]==2){
int location = i*c + j; // 用一个数标识二维数组的位置
queue.add(location);
map.put(location, 0);
}
}
}
// 烂橘子感染所有好橘子
int ret = 0;
while(!queue.isEmpty()){
int node = queue.remove();
int node_r = node/c;
int node_c = node%c;
for(int k=0; k<4; k++){
// 得到四周的橘子
int i = node_r + row[k];
int j = node_c + col[k];
if(i>=0 && i<r && j>=0 && j<c && grid[i][j]==1){ //记得判断ij的范围
// 好的橘子被感染
grid[i][j] = 2;
int location = i*c+j;
queue.add(location);
map.put(location, map.get(node)+1);
ret = map.get(location);
}
}
}
// 查看是否有好的橘子
for(int[] row:grid){
for(int v:row){
if(v==1){
return -1;
}
}
}
return ret;
}
}
注意:
queue
用于广度优先遍历;map
用于存储<腐烂的橘子location,第几秒烂的>- 在感染四周的好橘子的时候,要记得判断ij的范围
LinkedList
和ArrayDeque
的区别:-
操作:
ArrayDeque
:add()
remove()
LinkedList
:offer()
poll()
peek()
-
底层实现:
ArrayDeque
:基于可变长的数组和双指针来实现。
LinkedList
:基于双向链表来实现。 -
支持的元素
ArrayDeque
:不支持存储 null 数据。
LinkedList
:支持存储 null 数据。 -
引入版本
ArrayDeque
:在 JDK 1.6 中被引入。
LinkedList
:早在 JDK 1.2 中就已经存在。 -
插入性能
ArrayDeque
:插入时可能存在扩容过程,但均摊后的插入操作依然为 O(1)。
LinkedList
:每次插入数据时均需要申请新的堆空间,均摊性能相比更慢。
-
参考:
Java ArrayDeque 与 LinkedList 的区别?