持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第19天,点击查看活动详情
LeetCode 75 —— 200. 岛屿数量
一、题目描述:
给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:grid =
[ ["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"] ]
输出:1
示例 2:
输入:grid = [ ["1","1","0","0","0"], ["1","1","0","0","0"], ["0","0","1","0","0"], ["0","0","0","1","1"] ]
输出:3
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] 的值为 '0' 或 '1'
来源:力扣(LeetCode)
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二、思路分析:
-
这道题考察了什么思想?你的思路是什么?
对于这种岛屿问题,我们上次也讲了,可以使用DFS模板。对于网格上的DFS,首先我们要知道坐标为(r,c)的格子,其相邻的格子分别是:
(r-1, c)、(r+1, c)、(r, c-1)、(r, c+1)。然后我们就要确定递归终止条件,也就是判断坐标(r,c)是否在网格之中,也就是r<0 || c<0 || r>=len(grid) || c>=len(grid[0])。接下来需要注意的是,如何避免重复遍历,我们在图中遍历时,不可避免会遍历到重复的节点,因此需要将遍历过的节点做一个标记。于是我们将'1'赋值为'x'即可。 -
做题的时候是不是一次通过的,遇到了什么问题,需要注意什么细节?
不是啊。
如果照上面的思路,就会产生一个问题:
问题出现在哪呢?
如果将遍历过的节点设置为'x',就可能会导致r<0 || c<0 || r>=len(grid) || c>=len(grid[0]) || grid[r][c] == '0'一个条件都达不到,我们应该设置为以下r<0 || c<0 || r>=len(grid) || c>=len(grid[0]) || grid[r][c] != '1' {:
func numIslands(grid [][]byte) int {
result := 0
for i:=0; i<len(grid); i++{
for j:=0; j<len(grid[i]); j++{
if grid[i][j] == '1' {
result++
helper(grid,i,j)
}
}
}
return result
}
func helper(grid [][]byte,r,c int) {
if r<0 || c<0 || r>=len(grid) || c>=len(grid[0]) || grid[r][c] != '1' {
return
}
grid[r][c] = 'x'
helper(grid,r-1,c)
helper(grid,r,c-1)
helper(grid,r,c+1)
helper(grid,r+1,c)
}
-
有几种解法,哪种解法时间复杂度最低,哪种解法空间复杂度最低,最优解法是什么?其他人的题解是什么,谁的效率更好一些?用不同语言实现的话,哪个语言速度最快?
使用并查集实现:
type UnionFindSet struct { Parents []int // 每个结点的顶级节点 SetCount int // 连通分量的个数 } func (u *UnionFindSet) Init(grid [][]byte) { row := len(grid) col := len(grid[0]) count := row*col u.Parents = make([]int, count) for i := 0; i < row; i++ { for j := 0; j < col; j++ { u.Parents[i*col+j] = i*col+j if grid[i][j] == '1' { u.SetCount++ } } } } func (u *UnionFindSet) Find(node int) int { if u.Parents[node] == node { return node } root := u.Find(u.Parents[node]) u.Parents[node] = root return root } func (u *UnionFindSet) Union(node1 int, node2 int) { root1 := u.Find(node1) root2 := u.Find(node2) if root1 == root2 { return } if root1 < root2 { u.Parents[root1] = root2 } else { u.Parents[root2] = root1 } u.SetCount-- } // 心得:并查集是一种搜索算法(针对聚合的) func numIslands(grid [][]byte) int { // 创建并初始化并查集 u := &UnionFindSet{} row := len(grid) col := len(grid[0]) u.Init(grid) // 根据grid建立相应的并查集,并统计连通分量个数【每连接一次进行减一】 for i := 0; i < row; i++ { for j := 0; j < col; j++ { if grid[i][j] == '1' { // 如果周边四个方向也是1就进行union if i - 1 >= 0 && grid[i-1][j] == '1' { u.Union(i*col+j, (i-1)*col+j) } if i + 1 < row && grid[i+1][j] == '1' { u.Union(i*col+j, (i+1)*col+j) } if j - 1 >= 0 && grid[i][j-1] == '1' { u.Union(i*col+j, i*col+(j-1)) } if j + 1 < col && grid[i][j+1] == '1' { u.Union(i*col+j, i*col+(j+1)) } grid[i][j] = '0' } } } // 返回结果 return u.SetCount } 作者:bryson-2 链接:https://leetcode.cn/problems/number-of-islands/solution/bing-cha-ji-go-by-bryson-2/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
三、AC 代码:
func numIslands(grid [][]byte) int {
result := 0
for i:=0; i<len(grid); i++{
for j:=0; j<len(grid[i]); j++{
if grid[i][j] == '1' {
result++
helper(grid,i,j)
}
}
}
return result
}
func helper(grid [][]byte,r,c int) {
if r<0 || c<0 || r>=len(grid) || c>=len(grid[0]) || grid[r][c] == '0' {
return
}
grid[r][c] = '0'
helper(grid,r-1,c)
helper(grid,r,c-1)
helper(grid,r,c+1)
helper(grid,r+1,c)
}
四、总结:
深度优先搜索的时间复杂度和空间复杂度都为O(NM),其中 M*M 和 N*N 分别为行数和列数。
模板来源:
作者:掘金酱
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。