持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第27天,点击查看活动详情
🎈算法并不一定都是很难的题目,也有很多只是一些代码技巧,多进行一些算法题目的练习,可以帮助我们开阔解题思路,提升我们的逻辑思维能力,也可以将一些算法思维结合到业务代码的编写思考中。简而言之,平时进行的算法习题练习带给我们的好处一定是不少的,所以让我们一起来养成算法练习的习惯。今天练习的题目是一道中等难度的题目 -> 最短的路
题目描述
给你一个大小为 n x n 的二元矩阵 grid ,其中 1 表示陆地,0 表示水域。
岛 是由四面相连的 1 形成的一个最大组,即不会与非组内的任何其他 1 相连。grid 中 恰好存在两座岛 。
你可以将任意数量的 0 变为 1 ,以使两座岛连接起来,变成 一座岛 。
返回必须翻转的 0 的最小数目。
示例 1:
输入:grid = [[0,1],[1,0]]
输出:1
示例 2:
输入:grid = [[0,1,0],[0,0,0],[0,0,1]]
输出:2
示例 3:
输入:grid = [[1,1,1,1,1],[1,0,0,0,1],[1,0,1,0,1],[1,0,0,0,1],[1,1,1,1,1]]
输出:1
提示:
- n == grid.length == grid[i].length
- 2 <= n <= 100
- grid[i][j] 为 0 或 1
- grid 中恰有两个岛
思路分析
首先我们要先理解一下题目的意思,题目会给我们一个二维数组,数组代表的是一片区域,其中数值为1的代表陆地,0代表水域,上下或左右相邻的所有1可以构成一座岛。
如上图,我们可以这样找出两座岛,现在题目的要求是要我们找到两座岛之间最短的水路,因为提示中说到grid 中恰有两个岛,所以我们可以先找到一座岛左右坐标点,再判断从这些坐标点中到另一座岛的最短路径,具体步骤写法如下:
- 1、由一个点获取其完整岛的所有点
首先我们可以遍历这个数组,遇到第一个为1的位置时,我们可以从这个点向四周扩散,获取其所在完整岛的所有点,并给这个岛的所有点做上一个标记,这里我将第一个岛的所有点的值都改为2,用于和第二个岛作区分。
const getIsland = (x,y)=>{
if(x >= grid.length || x < 0 || y < 0 || y >= grid[0].length) return;
if(grid[x][y] == 0) return;
grid[x][y] = 2;
if(flag[x][y] !== Infinity) return;
flag[x][y] = true;
for(let i = 0; i < 4; i++){
getIsland(x + dx[i],y + dy[i]);
}
};
- 2、计算从另一个岛走到当前岛的最短路径
继续遍历剩余的点,遇到值为1时,则说明当前点为第二个岛的点,我们可以从该点出发去往第一个岛,这里我们使用bfs来进行遍历寻路。遍历过的点我们可以先将其步数记录起来,后面再次遍历到该点的时候如果之前的步数比当前的步数要小,则说明我们不需要继续往后遍历了,这样可以剪去很多重复无效的遍历。
const calStep = (queue) => {
while(queue.length){
const q = queue.shift();
if(grid[q[0]][q[1]] == 2){
res = Math.min(res,q[2]);
return;
}
if(flag[q[0]][q[1]] <= q[2]) continue;
flag[q[0]][q[1]] = q[2];
for(let i = 0; i < 4; i++){
const x = q[0] + dx[i];
const y = q[1] + dy[i];
if(x >= grid.length || x < 0 || y < 0 || y >= grid[0].length) continue;
if(flag[x][y] <= q[2]) continue;
if(grid[x][y] == 1){
calStep([[x,y,0]]);
}else{
queue.push([x,y,q[2] + 1]);
}
}
}
};
完整AC代码如下:
AC代码
/**
* @param {number[][]} grid
* @return {number}
*/
var shortestBridge = function(grid) {
const dx = [1,0,-1,0],dy = [0,1,0,-1];
let findFirstIsland = false;
let res = Infinity;
let flag = Array(grid.length).fill().map(() => Array(grid[0].length).fill(Infinity));
const getIsland = (x,y)=>{
if(x >= grid.length || x < 0 || y < 0 || y >= grid[0].length) return;
if(grid[x][y] == 0) return;
grid[x][y] = 2;
if(flag[x][y] !== Infinity) return;
flag[x][y] = true;
for(let i = 0; i < 4; i++){
getIsland(x + dx[i],y + dy[i]);
}
};
const calStep = (queue) => {
while(queue.length){
const q = queue.shift();
if(grid[q[0]][q[1]] == 2){
res = Math.min(res,q[2]);
return;
}
if(flag[q[0]][q[1]] <= q[2]) continue;
flag[q[0]][q[1]] = q[2];
for(let i = 0; i < 4; i++){
const x = q[0] + dx[i];
const y = q[1] + dy[i];
if(x >= grid.length || x < 0 || y < 0 || y >= grid[0].length) continue;
if(flag[x][y] <= q[2]) continue;
if(grid[x][y] == 1){
calStep([[x,y,0]]);
}else{
queue.push([x,y,q[2] + 1]);
}
}
}
};
for(let i = 0; i < grid.length; i++){
for(let j = 0; j < grid[i].length; j++){
if(grid[i][j] == 1){
if(!findFirstIsland){
findFirstIsland = true;
getIsland(i,j);
flag = Array(grid.length).fill().map(() => Array(grid[0].length).fill(Infinity));
}else{
calStep([[i,j,0]]);
return res - 1;
}
}
}
}
return res - 1;
};
说在后面
🎉这里是JYeontu,喜欢算法,GDCPC打过卡;热爱羽毛球,大运会打过酱油。毕业一年,两年前端开发经验,目前担任H5前端开发,算法业余爱好者,有空会刷刷算法题,平时喜欢打打羽毛球🏸 ,也喜欢写些东西,既为自己记录📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解🙇,写错的地方望指出,定会认真改进😊,在此谢谢大家的支持,我们下文再见🙌。