携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情
岛屿数量
给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外,你可以假设该网格的四条边均被水包围。
来源:力扣(LeetCode) 链接:leetcode.cn/problems/nu…
分析
- 把二维网格中根据给出的规则,即1的上下左右都是0或者边缘的情况下,算是一块陆地,求有多少块陆地
- 这道题目与图相关,在解有多少块陆地的时候, 可以通过搜索二维网格来求解,搜索的时候可以使用一个和grid一样的二维数组来记录是否访问过,因为本题是要求有多少块陆地,因此与原数组的值不是直接相关,因此可以在原数组上标识
DFS
- 从左上角开始,深度优先搜索
代码
var numIslands = function(grid){
let res = 0
const dfs = (x,y) => {
grid[x][y]=0
if(grid[x-1] && grid[x-1][y]) dfs(x-1,y)
if(grid[x+1] && grid[x+1][y]) dfs(x+1,y)
if(Number(grid[x][y-1]) dfs(x,y-1)
if(Number(grid[x][y+1]) dfs(x,y+1)
}
for(let i=0;i<grid.length;i++){
for(let j=0;j<grid[0].length;j++){
const temp = grid[i][j]
if(Number(temp)){
res ++
dfs(i,j)
}
}
}
return res
}
BFS
- 搜索的方式也可以是广度优先
代码
var numIslands = function(grid) {
let counter = 0;
function bfs(x,y) {
const queue = [[x,y]];
grid[x][y] = 0;
while(queue.length) {
for (const [x,y] of queue) {
// up
if (grid[x-1] && Number(grid[x-1][y])) {
console.log("up")
queue.push([x-1,y]);
grid[x-1][y] = 0;
}
//down
if (grid[x+1] && Number(grid[x+1][y])) {
console.log("down")
queue.push([x+1,y]);
grid[x+1][y] = 0;
}
// left
if (Number(grid[x][y-1])) {
console.log("left")
queue.push([x,y-1]);
grid[x][y-1] = 0;
}
//right
if (Number(grid[x][y+1])) {
console.log("right")
queue.push([x,y+1]);
grid[x][y+1] = 0;
}
queue.shift();
break;
}
}
}
for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid[0].length; j++) {
const cell = grid[i][j];
if (Number(cell)) {
counter++;
bfs(i,j);
}
}
}
return counter;
};
计算联通分量的个数
- 求所有的岛屿本身就是在计算图的联通分量个数
- 联通分量是图的一个属性,根据这个题目,可以分析这个图是个无向图
- 难点在于构造联通分量对应的并查集数据结构
代码
class UnionSet {
constructor(n = 100) {
this.father = new Array(n);
this.n = n;
this.init();
}
init() {
for (let i = 0; i < this.n; i++) {
this.father[i] = i;
}
}
find(x) {
return (this.father[x] =
this.father[x] === x ? x : this.find(this.father[x]));
}
merge(a, b) {
const fa = this.find(a);
const fb = this.find(b);
if (fa === fb) return false;
this.father[fb] = fa;
return true;
}
}
var numIslands = function(grid) {
const row = grid.length, col = grid[0].length;
const us = new UnionSet(row * col);
let result = 0;
for (let i = 0; i < row; i++) {
for(let j = 0; j < col; j++) {
if (grid[i][j] === "1") result++;
if (j + 1 < col && grid[i][j] === "1" && grid[i][j+1] === "1") {
const valid = us.merge(i * col + j, i * col + j + 1);
valid && result--;
}
if (i + 1 < row && grid[i][j] === "1" && grid[i+1][j] === "1") {
const valid = us.merge(i * col + j, (i + 1) * col + j);
valid && result--;
}
}
}
return result;
};
总结
- 在解二维数组相关的问题时,在无法化简数组的情况下,一般都把二维数组看作图
- 图分有向图和无向图,以及图有一些属性,比如本题的题干部分就是在讲形象的讲解什么是联通分量
- 图在分析的时候一般会用DFS和BFS,同时辅助一个数组来记录元素是否被访问过
- 今天也是有收获的一天