下面要分享的是一道来自leetcode的简单算法题,让我们一起进步成长。
使用语言:JavaScript
说明:使用到队列 + 多源广度搜索
题目:
// 994. 腐烂的橘子
// 在给定的网格中,每个单元格可以有以下三个值之一:
// 值 0 代表空单元格;
// 值 1 代表新鲜橘子;
// 值 2 代表腐烂的橘子。
// 每分钟,任何与腐烂的橘子(在 4 个正方向上)相邻的新鲜橘子都会腐烂。
// 返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1。
// 示例 1:
// 输入:[[2,1,1],[1,1,0],[0,1,1]]
// 输出:4
// 示例 2:
// 输入:[[2,1,1],[0,1,1],[1,0,1]]
// 输出:-1
// 解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个正向上。
// 示例 3:
// 输入:[[0,2]]
// 输出:0
// 解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。
// 提示:
// 1 <= grid.length <= 10
// 1 <= grid[0].length <= 10
// grid[i][j] 仅为 0、1 或 2
简单版:
function orangesRotting1(grid) {
if(grid == 0 || grid.length == 0) return 0;
var minute = 1;
var flag = true;
var r = grid.length;
var c = grid[0].length;
var note = new Array(r);
for(var i = 0; i < r; i ++) {
note[i] = new Array(c);
for(var j = 0; j < c; j ++) {
if(grid[i][j] == 2) {
note[i][j] = minute;
} else {
note[i][j] = 0;
}
}
}
while(flag) {
flag = false;
for(var i = 0; i < r; i ++) {
for(var j = 0; j < c; j ++) {
if(grid[i][j] == 2 && note[i][j] == minute) {
// console.log(i, j)
if(i > 0 && grid[i - 1][j] == 1) {
grid[i - 1][j] = 2;
note[i - 1][j] = minute + 1;
flag = true;
}
if(i < r - 1 && grid[i + 1][j] == 1) {
grid[i + 1][j] = 2;
note[i + 1][j] = minute + 1;
flag = true;
}
if(j > 0 && grid[i][j - 1] == 1) {
grid[i][j - 1] = 2;
note[i][j - 1] = minute + 1;
flag = true;
}
if(j < c - 1 && grid[i][j + 1] == 1) {
grid[i][j + 1] = 2;
note[i][j + 1] = minute + 1;
flag = true;
}
}
}
}
flag && minute ++;
// console.log(note);
}
for(var i = 0; i < r; i ++) {
for(var j = 0; j < c; j ++) {
if(grid[i][j] == 1) {
return -1;
}
}
}
return minute - 1;
}
进阶版:
基本解题思路:
先找出所有的腐烂的橘子,把他们都排到一个队列里面保存起来,然后从队列里按个获取出来,然后依据这个从队列里面获取出来的腐烂的橘子,判断它的周围(周围指:上下左右)是否有新鲜的橘子,如果有,就让这些新鲜的橘子腐烂,然后再把新腐烂的橘子在存到队列中,然后我们在从队列中获取橘子,在判断它的周围有没有新鲜的橘子…,依次搜索下去,找到队列里面没有橘子。
那么大家肯定会有疑问?
1.多源广度搜索的多源在哪里体现?
答:我们在第一次搜索时到的所有腐烂的橘子都放到了对队列里面,这些第一次搜索得到的橘子就是最初的源,如果有多个,就是多源了,而且这些源是属于同一层的(就是说,他们会同时使周围的新鲜橘子腐烂)
2.你都使用队列了,每一次取出一个腐烂的橘子,怎么会同时使周围的新鲜橘子腐烂呢?
答:队列的一个特性是先进先出,我们第一次搜索得到的橘子,就都排在队列的队头,队列里面第一个橘子去污染新鲜橘子后,就把这个橘子从队列中去掉,把新腐烂的橘子加到队尾,然后获取队列里面的第一个橘子,在去腐烂它周围新鲜的橘子,依次类推,只有在第一次搜索得到的腐烂橘子污染完成结束后,第二批腐烂的橘子才会去污染它周围的的新鲜橘子…
其次,这里的同时确实指的是同一个时刻,但是在代码实现运行上,我们如果是理解成同一个时段会更好。在第一个时段,队列里面的第一批腐烂的橘子去污染周围的新鲜橘子;第二个时段,队列里的第二批腐烂的橘子去污染周围新鲜的橘子…依次类推,腐烂的橘子污染完周围的新鲜橘子后,就从队头里去掉,所以在队列为空的时候,就污染完成
然后我们在判断是否还有存在新鲜的橘子。
3.那么,我们怎么记录它的污染全部新鲜橘子要多少分钟呢?
答:我们可以是使用一个map对象,可以把腐烂橘子在数组中的位置和在哪一个时间(即第几分钟)进行污染的形成一直对应关系。如果每一个腐烂的橘子(叫做A)污染了一个新鲜的橘子(叫做B),B的位置对应的时间段就是在A对应的时间段上加1.然后我们每一个有个橘子被污染,我们就记录下来,比较得出时间段的最大值。
function orangesRotting2(grid) {
if(grid == null || grid.length == 0) return -1;
// 用来扩大橘子的腐烂范围(上下左右)
// eg: [2, 3]是腐烂的橘子
// 那么就可以可能使[2, 4],[2, 1],[1, 3],[3, 3]这四个位置上的新鲜橘子腐烂
var a = [-1, 0, 1, 0];
var b = [0, -1, 0, 1];
var ans = 0;
var r = grid.length;
var c = grid[0].length;
var map = new Map();
var queue = new Array();
for(var i = 0; i < r; i ++) {
for(var j = 0; j < c; j ++) {
if(grid[i][j] == 2) {
var code = i * c + j;
// 把腐烂橘子排到队列的最后面
queue.push(code);
map.set(code, 0);
}
}
}
while(queue.length != 0) {
// 把队列的第一个给拿出来
var code = queue.shift();
// 计算出这个橘子的位置
i = Math.floor(code / c);
j = code % c;
for(var k = 0; k < 4; k ++) {
// 寻找橘子上下左右的位置
var nI = i + a[k];
var nJ = j + b[k];
if(nI < r && nI >= 0 && nJ >= 0 && nJ < c && grid[nI][nJ] == 1) {
// 使新鲜橘子腐烂
grid[nI][nJ] = 2;
var ncode = nI * c + nJ;
// 把腐烂橘子排到队列最后面
queue.push(ncode);
map.set(ncode, map.get(code) + 1);
ans = map.get(ncode) > ans ? map.get(ncode) : ans;
}
}
}
// 判断是否还有新鲜的橘子
for(var i = 0; i < r; i ++) {
for(var j = 0; j < c; j ++) {
if(grid[i][j] == 1) {
return -1
}
}
}
return ans;
}
高阶版:
基本思路:
1.先找出腐烂的橘子,同时找出新鲜橘子的个数,把腐烂的橘子存在一个队列里面
2.遍历这个队列,在声明一个队列nextQueue来存第二批腐烂的橘子,然后在让第一批腐烂的橘子去污染新鲜的橘子,把被污染的橘子保存在nextQueue里,并且新鲜橘子的个数要减1,在第一批腐烂的橘子完成无污染后,把nextQueue里面腐烂的橘子给到最初队列,然后继续遍历这个队列,循环操作,直到这个队列里没有值
最后,判断新鲜橘子的个数是不是为0既可。
function orangesRotting(grid) {
if(grid == null || grid.length == 0 || grid[0].length == 0) return -1;
const a = [0, 1, 0, -1];
const b = [1, 0, -1, 0];
let minute = 0;
let fresh = 0;
const R = grid.length;
const C = grid[0].length;
let queue = [];
for(let r = 0; r < R; r ++) {
for(let c = 0; c < C; c ++) {
if(grid[r][c] == 2) {
queue.push([r, c]);
} else if(grid[r][c] == 1){
fresh ++;
}
}
}
while(queue.length != 0 && fresh) {
let nextQueue = [];
while(queue.length != 0) {
let badOrange = queue.shift();
for(let k = 0; k < 4; k ++) {
let i = badOrange[0] + a[k];
let j = badOrange[1] + b[k];
if(i >= 0 && i < R && j >= 0 && j < C && grid[i][j] == 1) {
grid[i][j] = 2;
nextQueue.push([i, j]);
fresh --;
}
}
}
minute ++;
queue = nextQueue;
}
return fresh == 0 ? minute : -1;
}
如果本文章有何处错误,麻烦及时指出,我们一起成长,一起进步。
谢谢。