数据结构与算法-岛屿问题:网格结构中的 DFS(Python)

岛屿问题:网格结构中的 DFS

网格问题中的DFS

网格问题
由 m*n 个小方格组成一个网格,每个小方格与其 「上下左右」 四个方格认为是相邻的,要在这样的网格上进行某种搜索。
岛屿问题是网格DFS问题的典型代表。

网格结构中的DFS
二叉树的 DFS 有两个要素:「访问相邻结点」和「判断 base case」

  • 访问相邻结点:即递归调用左右子节点
  • 判断base case:root == None

因此,网格上的DFS

  • 相邻结点:上下左右四个
  • base case:下标越界,超出网格范围的格子

网格结构的 DFS 与二叉树的 DFS 最大的不同之处在于,遍历中可能遇到遍历过的结点,标记访问过的位置

岛屿问题解法

695. 岛屿的最大面积(中等)
在这里插入图片描述

class Solution:
    
    def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
        if not grid:
            return 0
        
    
        m = len(grid)
        n = len(grid[0])
        res = 0
        visited = [[0 for _ in range(n)] for _ in range(m)]

        steps = [(0,1),(0,-1),(1,0),(-1,0)]

        def inArea(i,j): #判断是否在网格内
            if i>=0 and i<m and j>=0 and j<n and grid[i][j]==1 and visited[i][j]==0:
                return True
            return False

        def dfs(i,j):#返回(i,j)出发的岛屿面积
            a = 1
            for step in steps:
                x = i+step[0]
                y = j+step[1]
                if inArea(x,y):
                    visited[x][y] = 1
                    a += dfs(x,y)
            return a
              

        for i in range(m):
            for j in range(n):
                if grid[i][j]==1 and visited[i][j]==0:
                    visited[i][j] = 1
                    res = max(res,dfs(i,j))
        return res   

827. 最大人工岛(困难)
在这里插入图片描述
方法一: 暴力DFS,超时
以每格海洋为源进行深度优先遍历,area=它上下左右所有岛屿面积之和+1

class Solution:
    def largestIsland(self, grid: List[List[int]]) -> int:
        if not grid:
            return 0
        
        m = len(grid)
        n = len(grid[0])
        res = 0
        steps = [(0,1),(0,-1),(1,0),(-1,0)]
        def dfs(i,j,visited):
            a = 1
            for step in steps:
                x = i+step[0]
                y = j+step[1]
                if x>=0 and x<m and y>=0 and y<n and grid[x][y]==1 and visited[x][y]==0:
                    visited[x][y] = 1
                    a += dfs(x,y,visited)
            return a
        flag = False
        for i in range(m):
            for j in range(n):
                if grid[i][j] == 0:
                    flag = True
                    visited = [[0 for _ in range(n)] for _ in range(m)]
                    res = max(res,dfs(i,j,visited))
        if flag:
            return res
        return m*n

方法二:
在方法一中重复计算了连通块
先计算出所有岛屿的面积,搜索哪个海洋格子相邻的岛屿面积最大,注意区分上下左右相邻的是否是同一岛屿(如下图所示)。
图1
思路:
第一次遍历,计算每个连通块的面积,分别赋成index,并用字典保存对应的面积;
第二次遍历,计算每个海洋格子周围的岛屿面积。
在这里插入图片描述

class Solution:
    def largestIsland(self, grid: List[List[int]]) -> int:
        if not grid:
            return 0
        
        m = len(grid)
        n = len(grid[0])
        res = 0
        steps = [(0,1),(0,-1),(1,0),(-1,0)]
        

        def dfs(i,j,idx):
            a = 1
            for step in steps:
                x = i+step[0]
                y = j+step[1]
                if x>=0 and x<m and y>=0 and y<n and grid[x][y]==1:
                    grid[x][y] = idx
                    a += dfs(x,y,idx)
            return a
        
        dic = {}
        idx = 1
        flag = False
        for i in range(m):
            for j in range(n):
                if grid[i][j] == 1:
                    idx += 1
                    grid[i][j] = idx
                    dic[idx] = dfs(i,j,idx)
        res = 0
        for i in range(m):
            for j in range(n):               
                if grid[i][j]==0:
                    area = 1                   
                    flag = True
                    seen = set()
                    for step in steps:
                        x = i+step[0]
                        y = j+step[1]
                        if x>=0 and x<m and y>=0 and y<n and grid[x][y]!=0:
                            key = grid[x][y]
                            if key not in seen:
                                area += dic[key]
                                seen.add(key)
                    res = max(res,area)

        if flag: 
            return res
        return m*n 

463. 岛屿的周长(简单)
在这里插入图片描述
dfs函数直接返回有以下几种情况:

  • 坐标不在网格内(周长+1)
  • 是海洋格子(周长+1)
  • 是已遍历过的陆地格子
class Solution:
    def islandPerimeter(self, grid: List[List[int]]) -> int:
        if not grid:
            return 0

        m = len(grid)
        n = len(grid[0])
        
        steps = [(0,1),(0,-1),(1,0),(-1,0)]
        
        def dfs(i,j):
            l = 0
            for step in steps:
                x = i+step[0]
                y = j+step[1]
                if x<0 or x>=m or y<0 or y>=n:
                    l += 1
                elif  x>=0 and x<m and y>=0 or y<n:
                    if grid[x][y] == 1:
                        grid[x][y] = 2
                        l += dfs(x,y)
                    elif grid[x][y] == 0:
                        l += 1
            return l


        res = 0
        for i in range(m):
            for j in range(n):
                if grid[i][j]==1:
                    grid[i][j] = 2
                    return dfs(i,j)
class Solution:
    def islandPerimeter(self, grid: List[List[int]]) -> int:
        if not grid:
            return 0

        m = len(grid)
        n = len(grid[0])
        
        steps = [(0,1),(0,-1),(1,0),(-1,0)]
        
        #先污染后治理,判断当前的格子
        def dfs(i,j):
            if i<0 or i>=m or j<0 or j>=n:
                return 1
            if grid[i][j] == 0:
                return 1
            if grid[i][j] != 1:
                return 0
            
            grid[i][j] = 2
            
            l = 0
            for step in steps:
                x = i+step[0]
                y = j+step[1]
                l += dfs(x,y)
            return l


        res = 0
        for i in range(m):
            for j in range(n):
                if grid[i][j]==1:
                    return dfs(i,j)

参考资料:
https://mp.weixin.qq.com/s?__biz=MzA5ODk3ODA4OQ==&mid=2648167208&idx=1&sn=d8118c7c0e0f57ea2bdd8aa4d6ac7ab7&chksm=88aa236ebfddaa78a6183cf6dcf88f82c5ff5efb7f5c55d6844d9104b307862869eb9032bd1f&token=1064083695&lang=zh_CN#rd

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值