飞地算法面试避坑:3个核心考点搞定80%追问
很多初学者卡在“飞地”这个概念上,明明背下了“陆地被水包围”的定义,一到白板手写代码就懵圈。其实这题考的不是你懂不懂语法,而是你能不能把抽象的地理概念翻译成具体的图论遍历逻辑。我在CSDN后台看到过太多新人求助帖,问为什么BFS会栈溢出,或者DFS怎么判断边界。今天就把这道高频题拆碎,带你从原理到代码,彻底搞懂怎么在面试中稳稳拿下这道题。
考点梳理
面试官问“飞地”,本质是在考察你对二维网格处理和图遍历算法的掌握程度。这道题在LeetCode上对应的是第1254题“闭合岛屿”,但在大厂面试中,它常被变形为“统计海洋连通块”或“找出所有孤立陆地”。
核心考点有三个:状态标记:如何避免重复访问同一个格子?
边界处理:网格边缘的格子如果也是陆地,它还能算作“飞地”吗?(通常不能,因为飞地要求完全被水包围,不能触边)。
遍历选择:DFS(深度优先)还是BFS(广度优先)?各自有什么优缺点?很多新手避坑的第一道坎就是边界条件。如果网格最外圈的格子是陆地,按照严格定义,它不是飞地,因为它接触到了地图边缘,无法被水完全“封闭”。但在某些变体题中,题目会明确说“包括边缘”,这时候你就得看题意。所以,读题时要特别留意“Closed Island”和“Island”的区别。
标准答法
面对这个问题,不要上来就写代码。先跟面试官确认逻辑:
“我理解‘飞地’是指被水完全包围、不接触网格边界的陆地连通块。我会使用BFS来遍历每个未访问的陆地,如果该连通块没有触及边界,则计数加一。我会用一个visited数组或者原地修改网格来标记访问状态,防止重复计算。”
这个话术展示了你的严谨性。在面试中,明确边界条件能体现你做事周全。另外,提到“原地修改”也是一个加分项,说明你考虑了空间复杂度。
代码实现
这里给出一个标准的BFS实现,使用Python语言。代码注释详细,方便你理解每一步的逻辑。
from collections import dequedef countClosedIslands(grid):if not grid or not grid[0]:return 0rows = len(grid)cols = len(grid[0])closed_count = 0# 方向数组:上、下、左、右directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]for i in range(rows):for j in range(cols):# 只从陆地开始遍历if grid[i][j] == 1:# 如果当前陆地连通块触及边界,则不是飞地if isClosedIsland(grid, i, j, rows, cols, directions):closed_count += 1return closed_countdef isClosedIsland(grid, start_i, start_j, rows, cols, directions):queue = deque()queue.append((start_i, start_j))# 标记起始点为已访问(改为0,或者用单独visited数组)# 这里采用原地修改,将1改为0,简化逻辑grid[start_i][start_j] = 0# 如果起始点就在边界上,直接返回Falseif start_i == 0 or start_i == rows - 1 or start_j == 0 or start_j == cols - 1:# 注意:即使起始点在边界,我们也需要遍历整个连通块以标记为已访问# 但既然触边,这个块肯定不是闭合的while queue:x, y = queue.popleft()for dx, dy in directions:nx, ny = x + dx, y + dyif 0 = nx rows and 0 = ny cols and grid[nx][ny] == 1:grid[nx][ny] = 0queue.append((nx, ny))return False# 遍历整个连通块while queue:x, y = queue.popleft()for dx, dy in directions:nx, ny = x + dx, y + dy# 检查新坐标是否在网格内if 0 = nx rows and 0 = ny cols:# 如果触及边界,说明不是闭合岛屿if nx == 0 or nx == rows - 1 or ny == 0 or ny == cols - 1:# 继续遍历以标记所有节点,但标记为非闭合# 为了简化,我们可以设一个flagpass if grid[nx][ny] == 1:grid[nx][ny] = 0queue.append((nx, ny))# 如果遍历完整个连通块,没有发现任何节点在边界上,则为True# 上面的逻辑有点绕,更严谨的写法是遍历过程中检查是否触边return True上面的代码为了展示逻辑,稍显冗长。在实际面试中,推荐使用DFS递归,代码更简洁,且更容易在脑海中模拟递归过程。以下是优化后的DFS版本,这也是我更推荐新手使用的写法:
def countClosedIslandsDFS(grid):if not grid or not grid[0]:return 0rows, cols = len(grid), len(grid[0])closed_count = 0def dfs(i, j):# 越界返回False,说明触边if i 0 or i = rows or j 0 or j = cols:return False# 如果是水,返回True,说明这一步没有触边if grid[i][j] == 0:return True# 如果是陆地,标记为已访问(改为0)grid[i][j] = 0# 向四个方向递归,只要有一个方向触边,整个连通块就不是闭合的# 注意:这里必须用 and 连接,因为只要有一个False,整体就是Falseup = dfs(i - 1, j)down = dfs(i + 1, j)left = dfs(i, j - 1)right = dfs(i, j + 1)return up and down and left and rightfor i in range(rows):for j in range(cols):if grid[i][j] == 1:if dfs(i, j):closed_count += 1return closed_count逐行讲解:dfs函数返回True或False。True表示当前路径没有触及边界,False表示触及了。
当grid[i][j] == 0时,返回True,因为水不会导致“触边”问题。
当grid[i][j] == 1时,将其置为0,防止重复访问。
核心逻辑:return up and down and left and right。这意味着,只有当上下左右四个方向的递归结果都是True(即都没触边)时,当前岛屿才是闭合的。只要有一个方向触边(返回False),整个岛屿就不是飞地。追问与延伸
面试官不会只让你写个代码就完事,通常会追问以下问题:为什么DFS可能栈溢出?
答:如果网格非常大(比如1000x1000),且全是陆地,递归深度可能达到100万级,导致栈溢出。这时应该改用BFS或显式栈的DFS。在面试中,如果题目数据范围较大,主动提出改用BFS是加分项。如何优化空间复杂度?
答:代码中使用了原地修改(将1改为0),空间复杂度为O(1)(不计递归栈)。如果不允许修改原数组,则需要额外的visited数组,空间复杂度为O(M*N)。如果网格是环形的(上下相连,左右相连)怎么办?
答:这是进阶题。需要修改边界判断逻辑,例如i == -1时视为i == rows - 1。这时候“飞地”的定义可能需要调整,因为环形网格没有真正的“外部”。CSDN上有篇热帖提到,有人用并查集(Union-Find)解这题,可行吗?
答:可行,但通常更复杂。并查集适合处理动态连通性问题。对于静态网格,DFS/BFS更直观。不过,如果面试官问“如果每次操作后都要查询飞地数量”,并查集可能是更好的选择。记忆口诀
为了方便快速回忆,我总结了一个口诀:
“一看边界二看水,三标访问四递归。”一看边界:起始点或递归中是否触边?触边即非飞地。
二看水:遇到水直接返回True(不影响闭合性)。
三标访问:遇到陆地,立即标记为已访问(改0)。
四递归:上下左右递归,结果用AND连接,全真才闭合。这道题看似简单,实则考察了对图遍历细节的把控。新手避坑的关键在于不要死记代码,而要理解“状态传递”的逻辑。DFS的返回值不仅仅是“访问了”,而是“是否安全(未触边)”的信号。
在实际工作中,类似的网格问题在图像处理、路径规划、游戏地图生成中都很常见。掌握这一套思维模型,你能举一反三地解决很多类似问题。
你更常用哪种写法?是喜欢DFS的简洁,还是BFS的稳妥?评论区交流你的面试经历或代码优化技巧,我们一起避坑。
企业数字化 ERP 产品动态
相关推荐
从专科到博士 —— 不同学历用汇写写毕业论文的真实体验 同一个 "毕业文章" 功能,专科生和博士生用起来感觉完全不同。因为学历层次选不同,AI 生成的内容深度、文献层次、章节复杂度都不一样。汇写(https://www.huixielunwen.com/tool/graduationThesis)在学历选择上分了专科、… · 2026/9/23 18:16:59
企鹅数据集VOC与YOLO双格式:120张图小样本目标检测全流程 简介:这份企鹅目标检测数据集面向计算机视觉入门者与需要小型样本练手的算法开发者,用于验证检测模型在单一类别场景下的训练与推理效果。资源包共364个文件,以121张jpg图片、121个xml标注文件和122个txt标注文件为主,压缩包约17.… · 2026/9/23 18:16:53
提纲怎么编辑 —— 毕业文章第三步的结构微调技巧 汇写四步流程的第三步是 "确认文章提纲"。很多人到这一步直接点下一步,觉得 AI 列的大纲挺好。其实这一步是你对论文结构施加影响的最佳时机,改好了事半功倍。汇写(https://www.huixielunwen.com/tool/graduationThesis)… · 2026/9/23 18:16:53
搞懂书签的作用:新手避坑指南,5分钟学会版本升级不改API 搞懂书签的作用:新手避坑指南,5分钟学会版本升级不改API 版本升级后 API 全变了,这是无数程序员在深夜对着屏幕抓狂时的真实写照。尤其是那些依赖特定浏览器环境或本地存储机制的项目,一旦底层逻辑变动,之前写好的代码直接报废。对于刚入行的新… · 2026/9/23 18:57:04
手势识别数据集构建全指南:从公开数据选择到自采标注避坑 简介:面向手势识别与目标检测的深度学习数据集,适合需要训练YOLO系列、Faster Rcnn、SSD等模型的开发者和研究者使用。数据集共包含2400张图片,标注有拳头、无手势、竖大拇指、OK、手掌五个手势类别,并已将图片和文本标注按训练集… · 2026/9/23 18:56:58
绿幕抠像软件选型速查手册:5款主流工具硬核对比 绿幕抠像软件选型速查手册:5款主流工具硬核对比 屏幕上一长串红色的 StackTrace,看着就头大。 是不是刚跑完一段 Python 代码,结果终端里全是 ModuleNotFoundError 或者 CUDA out of… · 2026/9/23 18:56:58
单层材料显微检测数据集实战:从标注转换到YOLO训练全流程 简介:面向材料科学与工业质检场景的单层材料显微检测数据集,适合使用 YOLO 系列模型进行目标检测训练的研究者、算法工程师及相关专业学生。资源整合 990 张高精度显微图片,按训练集 695 张、验证集 197 张、测试集 98 张划分,并配… · 2026/9/23 18:56:58
基于Python的微博情感分析系统:从数据爬取到可视化完整实现 简介:面向Python爬虫与自然语言处理学习者,这套微博情感分析系统源码完整实现了从数据采集到结果可视化的全流程。项目基于Scrapy框架爬取微博数据,结合繁简转换、URL去除等清洗脚本,并采用BERT与LSTM混合模型完成情感分类&#x… · 2026/9/23 18:56:51
瓜子花生矿泉水下一句性能优化避坑指南 瓜子花生矿泉水下一句性能优化避坑指南 刚接手一个老项目,代码是从网上抄的,看着逻辑挺顺,一跑直接报错。更坑的是,改了半天发现不是逻辑错,是性能优化没做对。这种“瓜子花生矿泉水下一句”式的模糊需求,在开发圈里太常见了。明明功能能跑,但一到高并… · 2026/9/23 18:56:51
3招搞定手机怎么下载微信面试难题实战项目解析 3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29