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

LeetCode 补拙笔记 日期:2026.05.29 题目:1559. 二维网格图中探测环

LeetCode 补拙笔记

0. 前言

  • 日期:2026.05.29
  • 题目:1559. 二维网格图中探测环
  • 难度:中等
  • 标签:并查集、图论、DFS

1. 题目理解

问题描述
给定一个二维字符网格grid,判断网格中是否存在由相同字符构成的环。
环的定义是:一条长度 ≥ 4 的路径,起点和终点为同一个格子,路径中不能直接回到上一步所在的格子。

示例

输入:grid =[["a","a","a","a"],["a","b","b","a"],["a","b","b","a"],["a","a","a","a"]]
输出:true
解释:外层的a和内层的b都各自形成了环。

2. 解题思路

核心观察

  • 环的存在等价于在无向图中,两个连通的节点被再次合并时,会形成环。
  • 可以用并查集(Union-Find)解决:遍历相邻格子,若字符相同则尝试合并,合并前发现两节点已连通,说明存在环。

算法步骤

  1. 初始化并查集,每个格子为独立集合。
  2. 遍历网格,对每个格子只检查右侧和下侧的邻居(避免重复检查)。
  3. 若当前格子与邻居字符相同,执行合并操作。
  4. 合并时若发现两节点已连通,说明形成了环,直接返回true
  5. 遍历结束未发现环,返回false

3. 代码实现

packagelc1559;classSolution{publicbooleancontainsCycle(char[][]grid){intn=grid.length;if(n==0)returnfalse;intm=grid[0].length;int[]parent=newint[n*m];for(inti=0;i<n*m;i++){parent[i]=i;}for(inti=0;i<n;i++){for(intj=0;j<m;j++){charc=grid[i][j];// 向右看if(j+1<m&&grid[i][j+1]==c){intu=i*m+j;intv=i*m+(j+1);if(union(parent,u,v)){returntrue;}}if(i+1<n&&grid[i+1][j]==c){intu=i*m+j;intv=(i+1)*m+j;if(union(parent,u,v)){returntrue;}}}}returnfalse;}privateintfind(int[]parent,intx){introot=x;while(parent[root]!=root){root=parent[root];}while(parent[x]!=root){intnext=parent[x];parent[x]=root;x=next;}returnroot;}privatebooleanunion(int[]parent,intx,inty){introotX=find(parent,x);introotY=find(parent,y);if(rootX==rootY){returntrue;}parent[rootY]=rootX;returnfalse;}}

4. 代码优化说明

(代码未做任何修改,仅添加注释讲解)

classSolution{publicbooleancontainsCycle(char[][]grid){intlen1=grid.length;intlen2=grid[0].length;// 并查集数组,索引表示格子编号,值为父节点int[]a=newint[len1*len2];a[0]=0;// 初始化第一行的父节点for(intj=1;j<len2;j++){// 若与左侧字符相同,继承左侧的父节点;否则父节点为自身if(grid[0][j]==grid[0][j-1]){a[j]=a[j-1];}else{a[j]=j;}}intk=len2;// 遍历后续每一行for(inti=1;i<len1;i++){// 初始化每行第一个格子的父节点if(grid[i][0]==grid[i-1][0]){a[k]=a[i*len2-len2];}else{a[k]=k;}k++;// 遍历每行后续的格子for(intj=1;j<len2;j++,k++){// 先处理左侧邻居if(grid[i][j]==grid[i][j-1]){a[k]=a[k-1];}else{a[k]=k;}// 再处理上方邻居,判断是否形成环if(grid[i][j]==grid[i-1][j]){// 若当前格子与上方格子已连通,则存在环if(fn(a,k)==fn(a,k-len2)){returntrue;}else{// 合并两个集合a[a[k-len2]]=a[k];}}}}returnfalse;}// 路径压缩的查找函数publicintfn(int[]a,intk){if(a[k]!=k){a[k]=fn(a,a[k]);}returna[k];}}

5. 复杂度分析

  • 时间复杂度O(n×m⋅α(n×m))O(n \times m \cdot \alpha(n \times m))O(n×mα(n×m))
    其中nnnmmm为网格的行列数,α\alphaα为阿克曼函数的反函数,近似为常数。并查集的查找和合并操作几乎是常数时间。
  • 空间复杂度O(n×m)O(n \times m)O(n×m)
    主要为并查集数组的开销。

6. 总结

  • 核心思路:并查集检测环,通过合并相同字符的相邻节点,利用连通性判断环的存在。
  • 优化后代码在遍历顺序和并查集初始化上做了简化,逻辑更紧凑。
  • 关键技巧:只检查右侧和下侧邻居,避免重复合并;路径压缩优化查找效率。
http://www.jsqmd.com/news/911752/

相关文章:

  • 专业级英雄联盟回放解析解决方案:跨版本兼容性深度技术解析
  • 实验20 自动灭火场景实验
  • 海思Hi3518E VPSS配置避坑指南:从GROUP到CHANNEL,手把手搞定视频处理子系统
  • 5分钟快速上手洛雪音乐助手:免费跨平台音乐聚合播放器终极指南
  • 郑州郑东新区家电维修清洗|维小达 专业空调、冰箱、洗衣机、热水器、电视、油烟机、灶具、消毒柜、小家电维修清洗一站式服务 - 维小达科技
  • 四步终极指南:用OpenCore Legacy Patcher让老Mac免费升级最新系统
  • 别让变量名拖后腿!C语言标识符命名规则详解(附ZZULIOJ 1138题实战解析)
  • 量子计算在动态平均场理论中的创新应用
  • 2026 年 Q1 云厂商财报增速亮眼,“卖算力”难撑利润,谁能过渡到“卖不可替代性”?
  • 基于树莓派与CNN的工业缺陷检测系统:从硬件搭建到模型部署全流程
  • 从手机屏幕到摄影打光:搞懂色温与显色性,让你的照片和视频告别‘阴间滤镜’
  • 基于ESP32与FreeRTOS的工业液体定量控制系统设计与实现
  • ESP32驱动CRT电视板与SHARP TFT屏:模拟视频系统改造全解析
  • 一键永久激活Windows和Office:KMS智能激活完整解决方案
  • 基于ESP32的DIY四轴飞行器:从硬件设计到PID控制全解析
  • 从胎儿到AI:用“知道”框架重新理解意识与感知的连续谱
  • StateFlow 与 SharedFlow:Google 为什么要设计两套 Flow?—— 从一次 tryEmit(false) 到 WindowLeaked,彻底理解 Flow 的设计思想
  • 面试官的提问与燕双非的回答:Java 技术栈在电商场景中的应用
  • 基于Arduino与MPU6050的模型火箭智能降落伞释放系统全解析
  • Arduino驱动RGB灯带:MOSFET选型、PWM调光与平滑色彩过渡实战
  • Aspose.Words for Java 实战:Word转PDF页码对不上?手把手教你排查和修复
  • 告别Eclipse插件!用Maven插件antlr4-maven-plugin搞定语法解析代码生成(附JDK8/11兼容方案)
  • 2026年5月最新|杭州全屋定制哪家好?本地源头工厂盘点,高性价比品牌选购指南 - 商业新知
  • Lindy财务自动化黄金窗口期仅剩47天:财政部新规倒逼Q3前完成自动化凭证链审计留痕
  • 基于ESP32与Node.js的物联网智能时钟:从架构设计到FreeRTOS任务调度
  • 终极指南:如何免费快速解码QQ音乐加密文件(qmcdump完整教程)
  • 别再手动调坐标了!OpenPnP导入Gerber/坐标文件后,用这3个Mark点搞定全板自动校正
  • Wallpaper Engine下载器:3步轻松获取Steam创意工坊动态壁纸的完整指南
  • 从PFD到VCO:手把手教你用TSMC 0.18um工艺仿真一个1.5GHz的电荷泵锁相环
  • Agent Skills 万千应用 · 第14篇_论文追踪 Skill:自动关注新论文,把资料变成判断