LeetCode 封闭岛屿数量问题详解:从DFS到DSU的四种解法

LeetCode 封闭岛屿数量问题详解:从DFS到DSU的四种解法

leetcode Leetcode solutions leetcode 项目地址: https://gitcode.com/gh_mirrors/leetcode1/leetcode

问题描述

给定一个由0(陆地)和1(水域)组成的二维网格,计算其中"封闭岛屿"的数量。封闭岛屿是指完全由水域包围的陆地区域,即岛屿的所有边界都不能接触网格的边缘。

解法一:深度优先搜索(DFS-I)

核心思路

使用DFS遍历每个陆地单元格,同时检查该陆地是否能够延伸到网格边界。如果不能延伸到边界,则说明这是一个封闭岛屿。

代码实现

class Solution:
    def closedIsland(self, grid: List[List[int]]) -> int:
        ROWS, COLS = len(grid), len(grid[0])
        directions = [[0, 1], [0, -1], [1, 0], [-1, 0]]
        visit = set()

        def dfs(r, c):
            if r < 0 or c < 0 or r == ROWS or c == COLS:
                return 0  # 到达边界,不是封闭岛屿
            if grid[r][c] == 1 or (r, c) in visit:
                return 1  # 是水域或已访问过
            
            visit.add((r, c))
            res = True
            for dx, dy in directions:
                if not dfs(r + dx, c + dy):
                    res = False
            return res

        res = 0
        for r in range(ROWS):
            for c in range(COLS):
                if not grid[r][c] and (r, c) not in visit:
                    res += dfs(r, c)
        return res

复杂度分析

  • 时间复杂度:O(m×n),每个单元格最多被访问一次
  • 空间复杂度:O(m×n),用于存储访问记录

解法二:深度优先搜索(DFS-II)

核心思路

先处理所有边缘陆地,将它们标记为水域,然后统计剩余陆地区域的数量。

代码实现

class Solution:
    def closedIsland(self, grid: list[list[int]]) -> int:
        ROWS, COLS = len(grid), len(grid[0])
        directions = [0, 1, 0, -1, 0]

        def dfs(r, c):
            if r < 0 or c < 0 or r == ROWS or c == COLS or grid[r][c] == 1:
                return
            grid[r][c] = 1
            for d in range(4):
                dfs(r + directions[d], c + directions[d + 1])

        # 先处理边缘陆地
        for r in range(ROWS):
            dfs(r, 0)
            dfs(r, COLS - 1)
        for c in range(COLS):
            dfs(0, c)
            dfs(ROWS - 1, c)

        # 统计内部岛屿
        res = 0
        for r in range(1, ROWS - 1):
            for c in range(1, COLS - 1):
                if grid[r][c] == 0:
                    dfs(r, c)
                    res += 1
        return res

复杂度分析

  • 时间复杂度:O(m×n)
  • 空间复杂度:O(m×n),递归栈空间

解法三:广度优先搜索(BFS)

核心思路

使用BFS替代DFS来遍历陆地,同样检查是否能到达边界。

代码实现

from collections import deque

class Solution:
    def closedIsland(self, grid: list[list[int]]) -> int:
        ROWS, COLS = len(grid), len(grid[0])
        directions = [[0, 1], [0, -1], [1, 0], [-1, 0]]
        visit = set()
        res = 0

        def bfs(r, c):
            q = deque([(r, c)])
            visit.add((r, c))
            is_closed = True

            while q:
                x, y = q.popleft()
                for dx, dy in directions:
                    nx, ny = x + dx, y + dy
                    if nx < 0 or ny < 0 or nx >= ROWS or ny >= COLS:
                        is_closed = False
                        continue
                    if grid[nx][ny] == 1 or (nx, ny) in visit:
                        continue
                    visit.add((nx, ny))
                    q.append((nx, ny))
            return is_closed

        for r in range(ROWS):
            for c in range(COLS):
                if grid[r][c] == 0 and (r, c) not in visit:
                    res += bfs(r, c)
        return res

复杂度分析

  • 时间复杂度:O(m×n)
  • 空间复杂度:O(m×n),用于队列和访问记录

解法四:并查集(DSU)

核心思路

使用并查集数据结构来管理陆地的连通性,特别处理与边界相连的陆地。

代码实现

class DSU:
    def __init__(self, n):
        self.Parent = list(range(n + 1))
        self.Size = [1] * (n + 1)

    def find(self, node):
        if self.Parent[node] != node:
            self.Parent[node] = self.find(self.Parent[node])
        return self.Parent[node]

    def union(self, u, v):
        pu, pv = self.find(u), self.find(v)
        if pu == pv:
            return False
        if self.Size[pu] >= self.Size[pv]:
            self.Size[pu] += self.Size[pv]
            self.Parent[pv] = pu
        else:
            self.Size[pv] += self.Size[pu]
            self.Parent[pu] = pv
        return True

class Solution:
    def closedIsland(self, grid: List[List[int]]) -> int:
        ROWS, COLS = len(grid), len(grid[0])
        N = ROWS * COLS
        def index(r, c):
            return r * COLS + c
        
        dsu = DSU(N)
        directions = [0, 1, 0, -1, 0]
        
        # 处理边界和连通区域
        for r in range(ROWS):
            for c in range(COLS):
                if grid[r][c] == 0:
                    for d in range(4):
                        nr, nc = r + directions[d], c + directions[d + 1]
                        if min(nr, nc) < 0 or nr == ROWS or nc == COLS:
                            dsu.union(N, index(r, c))
                        elif grid[nr][nc] == 0:
                            dsu.union(index(r, c), index(nr, nc))

        # 统计封闭岛屿
        res = 0
        rootN = dsu.find(N)
        for r in range(ROWS):
            for c in range(COLS):
                if grid[r][c] == 1:
                    continue
                node = index(r, c)
                root = dsu.find(node)
                if root == node and root != rootN:
                    res += 1
        return res

复杂度分析

  • 时间复杂度:O(m×n×α(m×n)),其中α是反阿克曼函数
  • 空间复杂度:O(m×n),用于存储并查集结构

总结

  1. DFS-I:直观易懂,适合初学者理解问题本质
  2. DFS-II:预处理边界,简化后续判断,代码更简洁
  3. BFS:适合处理大规模网格,避免递归栈溢出
  4. DSU:适合需要频繁查询连通性的场景,扩展性强

选择哪种方法取决于具体场景和个人偏好。对于面试和日常编程,掌握DFS和BFS两种方法即可应对大多数岛屿类问题。

leetcode Leetcode solutions leetcode 项目地址: https://gitcode.com/gh_mirrors/leetcode1/leetcode

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

段日诗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值