以岛屿问题为代表的网格类DFS问题(Python)

以岛屿问题为代表的网格类DFS问题(Python) 

问题链接

参考链接:力扣

在 LeetCode 中,「岛屿问题」是一个系列系列问题,比如:

200. 岛屿数量

695. 岛屿的最大面积

463. 岛屿的周长

1905. 统计子岛屿

1254. 统计封闭岛屿的数目

面试题 16.19. 水域大小

130. 被围绕的区域

面试题 08.10. 颜色填充

以上是我目前做到的岛屿类题目系列题。

以上的解题方法都是基于一个模板:

将基于树的DFS扩展到基于网格的DFS

基于树的DFS基本模板是:

def dfs(root):
    # 终止条件
    if not root:
        return
    dfs(root.left)    
    dfs(root.right)

二叉树只需递归调用左孩子和右孩子即可

而在网格图中,有上下左右(4个方向),有的题目中还有对角线(8个方向)

但是无论多少个方向,我们解题的大致思路都是一致的,即:

def dfs(grid, r, c):
    # 判断是否超出网格边界
    if not(r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0])):
        return 
    # 判断是否是岛屿(陆地)
    if grid[r][c] != 1:
        return 
    grid[r][c] = 2
    dfs(grid, r - 1, c)
    dfs(grid, r + 1, c)
    dfs(grid, r, c - 1)
    dfs(grid, r, c + 1)

岛屿的最大面积 与 岛屿周长问题详解可以参考顶部链接。 这里仅附上python代码

最大岛屿面积

class Solution(object):
    def maxAreaOfIsland(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        def dfs(grid, r, c):
            # 判定边界 
            if not (r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0])):
                return 0    
            # return 返回的值有时候是 空 有时候是具体值,需要根据题意判定
            if grid[r][c] != 1:  # 如果不是陆地或者已经标记过的陆地也跳过
                return 0
            grid[r][c] = 2  # 标记r,c处的陆地
            return 1  + dfs(grid, r - 1, c) + dfs(grid, r + 1, c) + dfs(grid, r, c + 1) + dfs(grid, r, c - 1)
        res = 0
        r = len(grid)
        c = len(grid[0])
        for i in range(r):
            for j in range(c):
                if grid[i][j] == 1:
                    area = dfs(grid, i, j)
                    res = max(area, res)
        return res

水域大小

        解题思路:

这道题与岛屿面积唯一的区别就在于 方向比 岛屿面积多了4个,即上下左右加四个对角,其他思考方式完全相同。

class Solution(object):
    def pondSizes(self, land):
        """
        :type land: List[List[int]]
        :rtype: List[int]
        """
        def dfs(land, r, c):
            if not(r >= 0 and r < len(land) and c >= 0 and c < len(land[0])):
                return 0
            
            if land[r][c] != 0:
                return 0
            land[r][c] = 1
            return 1 + dfs(land, r - 1, c) + dfs(land, r + 1, c) + dfs(land, r, c - 1) + dfs(land, r, c + 1) + \
            dfs(land, r - 1, c - 1) + dfs(land, r + 1, c - 1) + dfs(land, r + 1, c + 1) + dfs(land, r - 1, c + 1)
        

        r = len(land)
        c = len(land[0])

        res = []
        for i in range(r):
            for j in range(c):
                if land[i][j] == 0:
                    area = dfs(land, i, j)
                    res.append(area)
        res.sort()
        return res

岛屿周长

这道题只看代码很难理解!! 请参考力扣

class Solution(object):
    def islandPerimeter(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        def dfs(grid, r, l):
            if not (r >= 0 and r < len(grid) and l >= 0 and l < len(grid[0])):
                return 1
            if grid[r][l] == 0:
                return 1
            if grid[r][l] != 1:
                return 0
            grid[r][l] = 2
            return dfs(grid, r + 1, l) + dfs(grid, r - 1, l) + dfs(grid, r, l + 1) + dfs(grid, r, l - 1)
        r = len(grid)
        l = len(grid[0])
        for i in range(r):
            for j in range(l):
                if grid[i][j] == 1:
                    return dfs(grid,i,j)

        return 0


岛屿数量

        解题思路:

整体代码思路与上面两题一致,不同的地方在于在最后两层循环时,需要记录数量结果。

class Solution(object):
    def numIslands(self, grid):
        """
        :type grid: List[List[str]]
        :rtype: int
        """
        def dfs(grid, r, l):
            # 仅仅是统计岛屿数量,并不需要返回具体值。
            if not (r >= 0 and r < len(grid) and l >= 0 and l < len(grid[0])):
                return 
            if grid[r][l] != '1':
                return 
            grid[r][l] = '2'  # 细节的一点是 题目中给出的是字符串,我刚开始调代码,疯狂出错
            dfs(grid, r + 1, l)
            dfs(grid, r - 1, l)
            dfs(grid, r, l - 1)
            dfs(grid, r, l + 1)

        r = len(grid)
        l = len(grid[0])
        res = 0 
        for i in range(r):
            for j in range(l):
                if grid[i][j] == '1':
                    res += 1
                    # 用res统计岛屿数量,同一个岛屿的在一次遍历中都变成了2
                    dfs(grid, i, j)
        return res

颜色填充

这个题是一个标准的岛屿类问题,简直不能再标准了

class Solution(object):
    def floodFill(self, image, sr, sc, newColor):
        """
        :type image: List[List[int]]
        :type sr: int
        :type sc: int
        :type newColor: int
        :rtype: List[List[int]]
        """
        def dfs(image, r, l):
            if not (r >= 0 and r < len(image) and l >= 0 and l < len(image[0])):
                return 
            if image[r][l] != color:
                return 
            image[r][l] = newColor
            dfs(image, r - 1, l)
            dfs(image, r + 1, l)
            dfs(image, r, l - 1)
            dfs(image, r, l + 1)
        color = image[sr][sc]
        # 如果初始颜色值与新颜色相同直接返回即可
        if color == newColor:
            return image
        dfs(image, sr, sc)
        return image

这道题直接套用模板,不用做任何变化

子岛屿

        解题思路:

本题在模板的基础上进行了改进:

如果 grid2 的一个岛屿,被 grid1 的一个岛屿 完全 包含,也就是说 grid2 中该岛屿的每一个格子都被 grid1 中同一个岛屿完全包含,那么我们称 grid2 中的这个岛屿为 子岛屿 。

我们需要进行两次深搜,第一次寻找在grid2里面是陆地,而在grid1里面是海洋的点,并把整块陆地变成海洋(这样的陆地是不满足题意中完全包围要求的,即至少有一块没有被完全包含)

第二次深搜就在grid2里面正常深搜就可以,统计岛屿数量就可以得出答案。

class Solution(object):
    def countSubIslands(self, grid1, grid2):
        """
        :type grid1: List[List[int]]
        :type grid2: List[List[int]]
        :rtype: int
        """
        # 正常的网格深搜模板
        def dfs(grid, r, c):
            if not(r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0])):
                return 
            
            if grid[r][c] != 1:
                return 
            grid[r][c] = 0
            dfs(grid, r - 1, c)
            dfs(grid, r + 1, c)
            dfs(grid, r, c - 1)
            dfs(grid, r, c + 1)

        r = len(grid1)
        c = len(grid1[0])
        
        for i in range(r):
            for j in range(c):
                # 解题最最关键的一部 将grid2中未被完全包含的陆地变成海洋    
                if grid1[i][j] == 0 and grid2[i][j] == 1:
                    dfs(grid2, i, j)
        # 后面就是统计岛屿数量的代码了 CV一下就可以
        res = 0
        for i in range(r):
            for j in range(c):
                if grid2[i][j] == 1:
                    dfs(grid2, i, j)
                    res += 1
        return res

封闭岛问题

最后两个题目又是新的变种题,即封闭岛屿问题:

封闭岛屿的定义如下:

封闭岛是一个 完全 由1包围(左、上、右、下)的岛。即直接或者间接与网格边界相邻的陆地不算封闭岛。我们在解题前必须了解到这个定义的含义。

        解题思路:

  那就类似子岛屿问题,先把不是封闭岛的陆地块变成海洋,再统计封闭岛的数量即可。

  1.遍历网格的四周,寻找grid = ‘1’ 的陆地,并把陆地变为海洋(0)

r = len(grid)
c = len(grid[0])

for i in range(r):
    dfs(grid, i, 0)
    dfs(grid, i, c - 1)

for i in range(c):
    dfs(grid, 0, i)
    dfs(grid, r - 1, i)    

  2. 统计岛屿数量

整体代码:

class Solution(object):
    def closedIsland(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """

        def dfs(grid, r, c):
            if not(r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0])):
                return 
            
            if grid[r][c] != 0:
                return 
            grid[r][c] = 1
            dfs(grid, r - 1, c)
            dfs(grid, r + 1, c)
            dfs(grid, r, c - 1)
            dfs(grid, r, c + 1)

        r = len(grid)
        c = len(grid[0])

        for i in range(r):
            dfs(grid, i, 0)
            dfs(grid, i, c - 1)
        
        for i in range(c):
            dfs(grid, 0, i)
            dfs(grid, r - 1, i)    
        res = 0
        for i in range(r):
            for j in range(c):
                if grid[i][j] == 0:
                    dfs(grid, i, j)
                    res += 1
        return res

被围绕的区域

        解题思路:

和上一个题完全一致,没有任何变动,我也没有感情把他AC掉了

class Solution(object):
    def solve(self, board):
        """
        :type board: List[List[str]]
        :rtype: None Do not return anything, modify board in-place instead.
        """
        if not board:
            return board
        def dfs(board, r, c):
            if not (r >= 0 and r < len(board) and c >= 0 and c < len(board[0])):
                return 
            if board[r][c] != 'O':
                return
            board[r][c] = 'A'
            dfs(board, r - 1, c)
            dfs(board, r + 1, c)
            dfs(board, r, c + 1)
            dfs(board, r, c - 1)
        r = len(board)
        c = len(board[0])
        for i in range(r):
            dfs(board, i, 0)
            dfs(board, i, c - 1)
        for i in range(c):
            dfs(board, 0, i)
            dfs(board, r - 1, i)
        
        for i in range(r):
            for j in range(c):
                if board[i][j] == 'A':
                    board[i][j] = "O"
                else:
                    board[i][j] = 'X'
        return board

总结:

题解写的确实太少,没有办法把思路讲的特别清楚,也都是按照自己对模板,对题目的理解去写的。想先理解这个问题,可以先参考力扣,大佬的思路解法,“真是妙蛙种子吃着妙脆角妙进了米奇妙妙屋,妙到家了”。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值