LeetCode200 岛屿数量

57 阅读3分钟

leetcode.cn/problems/nu… image.png

解法一:DFS

思路分析:如果访问二维数组的过程中,遇到某个位置是水,那不用管它,它肯定不是岛。如果遇到某个位置是陆地,有两种情况:他可能四周都是水,如果四周都是水那就一个岛了,这毋庸置疑;但也可能四周有与其他陆地相邻紧挨着,这几个陆地连成更大的一片陆地,但不管怎么样,由于题目限定了最外的边界周围都是水,所以不管这一片陆地有多大,是什么歪歪扭扭的形状,它的四周一定是以水为边界的。因此有个结论就是:从看到一个二维数组的某个位置是陆地开始,我就知道有个岛存在了。

但问题又来了,我们计算总的岛屿数量的时候,我们知道”每个陆地都属于一个岛屿“的概念,于是对二维数组每个元素遍历,然后我们遇到了一个陆地,就统计岛屿数量+1。但是,相邻的陆地,其实是同属一个岛屿的,如果按照上面的代码遇到陆地就岛屿数量+1,这就重复计算了。

因此针对上面这个问题,当遍历二维数组的时候,遇到了陆地,我们除了统计岛屿数+1之外,我们还要从这个陆地开始寻找到不断相邻连着的陆地,然后将他们直接从陆地变成水,也就是直接将这个岛直接挖掉变成海。这样就不会有遍历元素时重复判断了。

这个挖陆地的过程是一个从二维数组某个位置的元素开始,不断探访四周的过程。是一个不断重复的过程,可以用深度优先搜索递归处理。

线性扫描整个二维网络,如果某个节点值为1,就以其为起点深度优先搜索DFS,搜索过程访问的每个节点标记为0,统计进行深度优先搜索的次数,即为岛屿数量。

为什么每次遇到陆地(节点值为1),都要用 DFS 算法把陆地「淹了」(节点值置为0)呢?主要是为了省事,避免维护 visited 数组。因为 dfs 函数遍历到值为 0 的位置会直接返回,所以只要把经过的位置都设置为 0,就可以起到不走回头路的作用。

func numIslands(grid [][]byte) int {
    res := 0
    for i := 0; i<len(grid); i++{
        for j:=0; j<len(grid[i]); j++{
            if grid[i][j] == '1'{
                // DFS的次数即为岛屿数量
                res++
                dfs(grid, i, j)
            }
        }
    }
    return res
}

func dfs(grid [][]byte, i, j int){
    if i < 0 || i >= len(grid) || j < 0 || j >= len(grid[0]){
        // 索引越界
        return
    }
    if grid[i][j] == '0'{
        // 本身已经是水,无需重复淹
        return
    }
    // 将位置(i,j)上的陆地淹没
    grid[i][j] = '0'
    // 递归淹没上下左右的陆地
    dfs(grid, i-1, j)
    dfs(grid, i+1, j)
    dfs(grid, i, j-1)
    dfs(grid, i, j+1)
}