当前位置: 首页 > news >正文

LeetCode200.岛屿数量

文章目录

    • 问题描述
      • 示例
    • 算法原理
      • 核心思想
      • Python 实现代码
    • 执行流程详解
      • 示例 1 分析
        • 1. 初始化阶段
        • 2. 初始化并查集
        • 3. 合并相邻陆地
        • 4. 计算结果
    • 关键技术点解析
      • 1. 二维坐标映射
      • 2. 路径压缩优化
      • 3. 相邻关系判断
    • 复杂度分析
    • 总结

问题描述

给你一个由 ‘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

算法原理

这个问题可以通过**并查集(Union-Find)**数据结构高效解决。核心思路是将每个陆地格子视为一个节点,通过相邻关系进行合并操作,最终统计连通分量的数量。

核心思想

  1. 二维坐标映射 :将二维网格的每个格子映射到一维数组( index = row * cols + col )
  2. 并查集操作 :遍历所有陆地格子,将相邻的陆地合并到同一个集合
  3. 统计结果 :最终连通分量的数量就是岛屿的数量

Python 实现代码

from typing import List class Solution: def numIslands(self, grid: List[List[str]]) -> int: """ 计算岛屿数量 参数: grid: 二维网格,'1'表示陆地,'0'表示水 返回: 岛屿的数量 """ if not grid or not grid[0]: return 0 n = len(grid) # 行数 m = len(grid[0]) # 列数 # 初始化并查集 father = {} # 父节点字典 sets = 0 # 岛屿数量 def index(a: int, b: int) -> int: """将二维坐标映射到一维索引""" return a * m + b def find(i: int) -> int: """查找节点的根节点(带路径压缩)""" if i != father[i]: father[i] = find(father[i]) # 路径压缩 return father[i] def union(a: int, b: int, c: int, d: int) -> None: """合并两个陆地格子""" nonlocal sets fx = find(index(a, b)) fy = find(index(c, d)) if fx != fy: father[fx] = fy sets -= 1 # 合并后岛屿数量减1 # 初始化:每个陆地格子自成一个集合 for i in range(n): for j in range(m): if grid[i][j] == '1': idx = index(i, j) father[idx] = idx sets += 1 # 遍历所有格子,合并相邻的陆地 for i in range(n): for j in range(m): if grid[i][j] == '1': # 检查左边 if j > 0 and grid[i][j - 1] == '1': union(i, j, i, j - 1) # 检查上边 if i > 0 and grid[i - 1][j] == '1': union(i, j, i - 1, j) return sets

执行流程详解

示例 1 分析

grid = [ ['1','1','1','1','0'], ['1','1','0','1','0'], ['1','1','0','0','0'], ['0','0','0','0','0'] ]
1. 初始化阶段
m = 5 # 列数 father = {} # 父节点字典 sets = 0 # 初始岛屿数量为0
2. 初始化并查集

遍历所有格子,为每个陆地格子创建独立的集合:

# 第一行 (i=0) grid[0][0] = '1' → father[0] = 0, sets = 1 grid[0][1] = '1' → father[1] = 1, sets = 2 grid[0][2] = '1' → father[2] = 2, sets = 3 grid[0][3] = '1' → father[3] = 3, sets = 4 grid[0][4] = '0' → 跳过 # 第二行 (i=1) grid[1][0] = '1' → father[5] = 5, sets = 5 grid[1][1] = '1' → father[6] = 6, sets = 6 grid[1][2] = '0' → 跳过 grid[1][3] = '1' → father[8] = 8, sets = 7 grid[1][4] = '0' → 跳过 # 第三行 (i=2) grid[2][0] = '1' → father[10] = 10, sets = 8 grid[2][1] = '1' → father[11] = 11, sets = 9 grid[2][2] = '0' → 跳过 grid[2][3] = '0' → 跳过 grid[2][4] = '0' → 跳过 # 第四行 (i=3) 全部是'0',跳过

初始化完成后 : sets = 9 (共有 9 个陆地格子)

3. 合并相邻陆地

第一次循环 (i=0, j=0)

grid[0][0] = '1' # 检查左边:j > 0 不成立,跳过 # 检查上边:i > 0 不成立,跳过

第二次循环 (i=0, j=1)

grid[0][1] = '1' # 检查左边:grid[0][0] = '1' → union(0, 1, 0, 0) find(1) → 1, find(0) → 0 father[1] = 0, sets = 8

第三次循环 (i=0, j=2)

grid[0][2] = '1' # 检查左边:grid[0][1] = '1' → union(0, 2, 0, 1) find(2) → 2, find(1) → find(0) → 0 father[2] = 0, sets = 7

第四次循环 (i=0, j=3)

grid[0][3] = '1' # 检查左边:grid[0][2] = '1' → union(0, 3, 0, 2) find(3) → 3, find(2) → find(0) → 0 father[3] = 0, sets = 6

第五次循环 (i=1, j=0)

grid[1][0] = '1' # 检查左边:j > 0 不成立,跳过 # 检查上边:grid[0][0] = '1' → union(1, 0, 0, 0) find(5) → 5, find(0) → 0 father[5] = 0, sets = 5

第六次循环 (i=1, j=1)

grid[1][1] = '1' # 检查左边:grid[1][0] = '1' → union(1, 1, 1, 0) find(6) → 6, find(5) → find(0) → 0 father[6] = 0, sets = 4 # 检查上边:grid[0][1] = '1' → union(1, 1, 0, 1) find(6) → find(0) → 0, find(1) → find(0) → 0 已经在同一集合,跳过

后续循环

  • 所有剩余的陆地格子都会通过 find 操作找到根节点 0
  • 最终所有陆地格子都合并到同一个集合
4. 计算结果
return sets = 1

关键技术点解析

1. 二维坐标映射

def index(a: int, b: int) -> int: return a * m + b
  • 将二维坐标 (row, col) 映射到一维索引
  • 便于在并查集中使用数组存储

2. 路径压缩优化

def find(i: int) -> int: if i != father[i]: father[i] = find(father[i]) # 路径压缩 return father[i]
  • 每次查找时更新父节点,使后续查找更快
  • 时间复杂度接近 O(1)

3. 相邻关系判断

# 只检查左边和上边,避免重复合并 if j > 0 and grid[i][j - 1] == '1': union(i, j, i, j - 1) if i > 0 and grid[i - 1][j] == '1': union(i, j, i - 1, j)
  • 只需检查两个方向(左和上),避免重复处理
  • 因为右和下方向的格子会在后续循环中处理

复杂度分析

  • 时间复杂度 :O(nm α(nm)),其中 n 是行数,m 是列数,α是阿克曼函数的反函数
  • 空间复杂度 :O(nm),用于存储并查集的父节点数组

总结

通过这个详细的解析,我们可以清晰地看到算法如何将复杂的岛屿计数问题转化为连通分量的计算,从而高效地得到岛屿的数量。并查集数据结构在这个问题中发挥了关键作用,使得我们能够在接近线性的时间复杂度内解决问题。

核心要点 :

  1. 理解二维坐标到一维索引的映射
  2. 掌握并查集的基本操作(find 和 union)
  3. 理解连通分量数的物理意义
  4. 只检查两个方向(左和上)避免重复处理

这个算法不仅适用于岛屿数量问题,还可以推广到其他需要判断连通性的网格问题。

http://www.jsqmd.com/news/547584/

相关文章:

  • MinerU开源模型实战教程:用MinerU构建企业知识库文档自动入库预处理管道
  • 深耕工业影像20年:中之网科技宣传片制作专业测评报告
  • OpenClaw安全实践:GLM-4.7-Flash本地化部署的权限控制指南
  • Python 3.15 JIT性能实测报告:循环密集型任务提速3.2×,但91%开发者正误用@jit导致启动延迟激增200ms——你中招了吗?
  • 汽车OTA技术原理与安全实现详解
  • 别再为工业网络头疼了!用TSN的CQF和TAS机制,5分钟搞懂如何混合传输周期与非周期数据
  • 三种建图方式Python实现
  • 把 SAP Fiori 后端授权模型讲透:从 PFCG、Catalog 到 SU24 的一条完整链路
  • SD玩家必备:5个提升Lora使用效率的ComfyUI隐藏技巧(含More_Details等实战案例)
  • 【大模型调优】彻底洗掉论文“机器味”:DeepSeek/Kimi/豆包专属降AI指令与保姆级工作流
  • Cogito-v1-preview-llama-3B实战:5分钟上手,生成专业专利权利要求书
  • 搞懂 SAP Fiori 前端服务器授权模型:从看得见应用,到真正拿到数据
  • Guohua Diffusion快速体验:4090D显卡优化,开箱即用的国画生成神器
  • NaViL-9B惊艳案例集:10张复杂测试图的图文理解结果全公开
  • 5分钟掌握AI足球分析:从视频到战术洞察的完整解决方案
  • GLM-4-9B-Chat-1M镜像评测:vLLM部署效率如何?Chainlit前端体验分享
  • OpenClaw人人养虾:接入Matrix
  • 降AIGC哪家强?2026零成本保姆级教程:DeepSeek/Kimi/豆包专属降重指令实测与差异解析
  • 2026-03-27:替换至多一个元素后最长非递减子数组。用go语言,给定一个整数数组 nums。 你最多只能选择其中一个位置的元素,把它改成任意整数(也可以选择不改)。 在允许这种“最多一次改动”的
  • 显卡GOP
  • 手把手教你用ModelEngine的MCP协议,5分钟集成外部API打造专属AI助手
  • 遥感智能解译新纪元:GeoSeg破解地物识别效率瓶颈的技术革新
  • Nanbeige4.1-3B基础教程:tokenizer.pad_token缺失问题修复与chat template适配
  • 《计算机网络》再学习
  • AOP 代理对象的诞生时刻:Bean 生命周期中的“夺舍”瞬间
  • 【日语学习-日语知识点小记-日本語体系構造-JLPT-N2前期阶段-第一阶段(20):万事有始有终】
  • Python原生AOT安全编译实战:手把手复现CVE-2026-1847绕过防护、并部署可信执行环境(TEE)签名链
  • 自媒体人的秘密武器:OpenClaw+nanobot自动生成视频字幕文件
  • ROS2 Control
  • 豆包AI视频去水印,我试了几个简单方法,手机就能搞定