news 2026/6/17 4:08:56

代码随想录算法训练营Day44 | 99.岛屿数量、100.岛屿的最大面积

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
代码随想录算法训练营Day44 | 99.岛屿数量、100.岛屿的最大面积

KamaCoder99.岛屿数量

99. 计数孤岛

1.思路

DFS 深搜解法

解决此问题的核心思想是DFS。当我们遍历网格时,每遇到一个未被访问过的陆地(1),就意味着发现了一个新的岛屿。此时,我们需要从这个点出发,通过DFS找到所有与它相连的陆地,并将它们全部标记为“已访问”,以避免重复计算。

方式一

main 和 dfs 分工明确。在 main 函数中发现新岛屿后,先标记起点 used[i][j] = true,再调用 dfs;dfs 函数 不处理传入的起点,只负责标记并递归访问其相邻节点。

dfs 函数的正确执行依赖于 main 函数的预处理。如果忘记在main中标记used[i][j],可能会导致无限递归或逻辑错误。

#include <iostream> #include <vector> using namespace std; int dir[4][2]={0,-1,-1,0,0,1,1,0}; void dfs(vector<vector<int>>&graph,vector<vector<bool>>&used,int x,int y){ for(int i=0;i<4;i++){ int nextx=x+dir[i][0]; int nexty=y+dir[i][1]; if(nextx<1 || nextx>=graph.size() || nexty<1 || nexty>=graph[0].size()) continue; // 越界了,直接跳过 if(graph[nextx][nexty]==1 && !used[nextx][nexty]){ used[nextx][nexty]=true; // 只标记并访问邻居 dfs(graph,used,nextx,nexty); } } } int main(){ int n,m;cin>>n>>m; vector<vector<int>> graph(n+1,vector<int>(m+1,0)); vector<vector<bool>> used(n+1,vector<bool>(m+1,false)); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>graph[i][j]; } } int res=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(graph[i][j]==1 && !used[i][j]){ res++; // 遇到没访问过的陆地,+1 used[i][j]=true; // 关键点:main负责标记起点 dfs(graph,used,i,j); } } } cout<<res; return 0; }
方式二

dfs 高度封装。在 main 函数中发现新岛屿后,直接调用 dfs,由 dfs 内部处理标记;dfs 函数先处理传入的起点(检查、标记),再递归访问其相邻节点。

main函数只需要“发现”一个新起点,然后把整个探索任务“外包”给dfs

dfs函数没有前置条件,自身逻辑完整,调用更安全,也更容易在其他场景中复用。

#include <iostream> #include <vector> using namespace std; int dir[4][2]={0,-1,-1,0,0,1,1,0}; void dfs(vector<vector<int>>&graph,vector<vector<bool>>&used,int x,int y){ if(graph[x][y]==0 || used[x][y]) return; // 终止条件:访问过的节点 或者 遇到海水 used[x][y]=true; // 标记访问过 for(int i=0;i<4;i++){ int nextx=x+dir[i][0]; int nexty=y+dir[i][1]; if(nextx<1 || nextx>=graph.size() || nexty<1 || nexty>=graph[0].size()) continue; dfs(graph,used,nextx,nexty); } } int main(){ int n,m;cin>>n>>m; vector<vector<int>> graph(n+1,vector<int>(m+1,0)); vector<vector<bool>> used(n+1,vector<bool>(m+1,false)); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>graph[i][j]; } } int res=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(graph[i][j]==1 && !used[i][j]){ res++; dfs(graph,used,i,j); } } } cout<<res; return 0; }

BFS 广搜解法

使用 BFS 最重要的一点就是要清楚在什么时候将一个节点标记为used=true

错误做法:从队列中取出节点时再标记。同一个节点可能被多个邻居加入队列,导致队列中出现大量重复节点,严重影响性能。

// 错误示例 while(!que.empty()){ pair<int,int> cur = que.front(); que.pop(); used[cur.first][cur.second] = true; // <-- 错误的标记时机! for(...){ // ... 检查邻居 ... if(graph[nextx][nexty]==1 && !used[nextx][nexty]){ que.push({nextx,nexty}); // 邻居A和B可能同时看到C,并都把C加入队列 } } }

正确做法:在将节点加入队列之前就立即标记。一旦一个节点被标记,任何其他邻居再看到它时,!used[nextx][nexty]条件就不满足,从而保证了每个节点最多只被加入队列一次。

// 正确示例 if(graph[nextx][nexty]==1 && !used[nextx][nexty]){ used[nextx][nexty]=true; // <-- 1. 立即标记 que.push({nextx,nexty}); // <-- 2. 再加入队列 }
#include <iostream> #include <vector> #include <queue> using namespace std; int dir[4][2]={0,-1,-1,0,0,1,1,0}; void bfs(vector<vector<int>>&graph,vector<vector<bool>>&used,int x,int y){ queue<pair<int,int>> que; que.push({x,y}); used[x][y]=true; // 只要加入队列,立刻标记 while(!que.empty()){ pair<int,int> cur = que.front();que.pop(); for(int i=0;i<4;i++){ int nextx=cur.first+dir[i][0]; int nexty=cur.second+dir[i][1]; if(nextx<1 || nextx>=graph.size() || nexty<1 || nexty>=graph[0].size()) continue; if(graph[nextx][nexty]==1 && !used[nextx][nexty]){ used[nextx][nexty]=true; // 只要加入队列立刻标记 que.push({nextx,nexty}); } } } } int main(){ int n,m;cin>>n>>m; vector<vector<int>> graph(n+1,vector<int>(m+1,0)); vector<vector<bool>> used(n+1,vector<bool>(m+1,false)); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>graph[i][j]; } } int res=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(graph[i][j]==1 && !used[i][j]){ res++; bfs(graph,used,i,j); } } } cout<<res; return 0; }

2.思考

方式一:隐式终止条件,函数本身不包含对当前节点的终止检查,假定传入的节点是有效的。终止条件的实现是通过在递归调用之前,严格“过滤”掉所有无效的下一步来完成的。如果没有任何一个邻居满足递归条件,for循环会正常结束,函数自然返回,从而实现了终止。

方式二:显式终止条件,函数在执行任何实质性操作之前,首先检查当前状态是否满足递归继续的条件。如果不满足,就立即终止(return),不再向下执行。

特性BFS (队列实现)DFS (递归实现)
核心结构while循环 +queue递归函数调用(隐式栈)
标记时机入队时标记递归前标记(或在递归函数内部处理)
遍历路径一层一层,逐层扩散。一条路走到黑,再回溯。
空间风险队列可能很大(岛屿很宽时)。递归可能很深(岛屿很长时),有栈溢出风险。
代码风格迭代,逻辑更“平铺直叙”。递归,代码更简洁。

3.Reference:99. 岛屿数量 | 代码随想录


KamaCoder100.最大岛屿的面积

100. 最大岛屿的面积

1.思路

DFS

写法一

隐式终止条件

DFS 处理当前节点的相邻节点,即在主函数遇到岛屿就计数为1,DFS 处理接下来的相邻陆地

#include <iostream> #include <vector> using namespace std; int res; int dir[4][2]={0,-1,-1,0,0,1,1,0}; void dfs(vector<vector<int>>&graph,vector<vector<bool>>&used,int x,int y){ for(int i=0;i<4;i++){ int nextx=x+dir[i][0]; int nexty=y+dir[i][1]; if(nextx<1 || nextx>=graph.size() || nexty<1 || nexty>=graph[0].size()) continue; if(graph[nextx][nexty]==1 && !used[nextx][nexty]){ res++; used[nextx][nexty]=true; dfs(graph,used,nextx,nexty); } } } int main(){ int n,m;cin>>n>>m; vector<vector<int>>graph(n+1,vector<int>(m+1,0)); vector<vector<bool>>used(n+1,vector<bool>(m+1,false)); int ans=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>graph[i][j]; } } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(graph[i][j]==1 && !used[i][j]){ res=1; // 因为dfs处理下一个节点,所以这里遇到陆地了就先计数,dfs处理接下来的相邻陆地 used[i][j]=true; dfs(graph,used,i,j); // 将与其链接的陆地都标记上 true ans=max(ans,res); } } } cout<<ans; return 0; }
写法二

显式终止条件

DFS 处理当前节点,即在主函数遇到岛屿就计数为0,DFS 处理接下来的全部陆地

#include <iostream> #include <vector> using namespace std; int res; int dir[4][2]={0,-1,-1,0,0,1,1,0}; void dfs(vector<vector<int>>&graph,vector<vector<bool>>&used,int x,int y){ if(graph[x][y]==0 || used[x][y]) return; used[x][y]=1; // 标记访问过 res++; for(int i=0;i<4;i++){ int nextx=x+dir[i][0]; int nexty=y+dir[i][1]; if(nextx<1 || nextx>=graph.size() || nexty<1 || nexty>=graph[0].size()) continue; dfs(graph,used,nextx,nexty); } } int main(){ int n,m;cin>>n>>m; vector<vector<int>>graph(n+1,vector<int>(m+1,0)); vector<vector<bool>>used(n+1,vector<bool>(m+1,false)); int ans=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>graph[i][j]; } } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(graph[i][j]==1 && !used[i][j]){ res=0; // 因为dfs处理当前节点,所以遇到陆地计数为0,进dfs之后在开始从1计数 dfs(graph,used,i,j); // 将与其链接的陆地都标记上 true ans=max(ans,res); } } } cout<<ans; return 0; }

BFS

每次开始探索一个新的岛屿时,面积计数器都会从1开始重新计算

#include <iostream> #include <vector> #include <queue> using namespace std; int res; int dir[4][2]={0,-1,-1,0,0,1,1,0}; void bfs(vector<vector<int>>&graph,vector<vector<bool>>&used,int x,int y){ queue<pair<int,int>>que; que.push({x,y}); used[x][y]=true; // 加入队列就意味节点是陆地可到达的点 while(!que.empty()){ pair<int,int> cur=que.front();que.pop(); for(int i=0;i<4;i++){ int nextx=cur.first+dir[i][0]; int nexty=cur.second+dir[i][1]; if(nextx<1 || nextx>=graph.size() || nexty<1 || nexty>=graph[0].size()) continue; if(graph[nextx][nexty]==1 && !used[nextx][nexty]){ res++; used[nextx][nexty]=true; que.push({nextx,nexty}); } } } } int main(){ int n,m;cin>>n>>m; vector<vector<int>>graph(n+1,vector<int>(m+1,0)); vector<vector<bool>>used(n+1,vector<bool>(m+1,false)); int ans=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>graph[i][j]; } } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(graph[i][j]==1 && !used[i][j]){ res=1; bfs(graph,used,i,j); // 将与其链接的陆地都标记上 true ans=max(ans,res); } } } cout<<ans; return 0; }

2.思考

这道题在 岛屿数量 这道题上整体思路没有变化,只是把计算的岛屿数量改成了计算最大的某个岛屿,思路还是很清晰的,重点掌握 DFS 的两种解法,要么使用隐式终止条件,要么使用显式终止条件,二者在主函数中调用前 res 的初始化也是不同的。

3.Reference:100. 岛屿的最大面积 | 代码随想录

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/12 9:13:44

LeetCode 98. 验证二叉搜索树 解题总结

目录 一、方法一&#xff1a;递归边界约束法&#xff08;范围校验&#xff09; 1. 核心思想 2. 完整实现代码 3. 重点 & 难点 二、方法二&#xff1a;中序遍历法&#xff08;利用 BST 特性&#xff09; 1. 核心思想 2. 实现代码 版本 1&#xff1a;递归中序遍历&…

作者头像 李华
网站建设 2026/6/15 18:09:44

15B参数多模态模型Apriel-1.5-Thinker:中小企业AI部署新选择

导语 【免费下载链接】Apriel-1.5-15b-Thinker 项目地址: https://ai.gitcode.com/hf_mirrors/unsloth/Apriel-1.5-15b-Thinker ServiceNow推出的15B参数多模态推理模型Apriel-1.5-Thinker&#xff0c;以其仅需单GPU即可运行的轻量化特性和媲美大模型的推理能力&#x…

作者头像 李华
网站建设 2026/6/15 11:27:44

Dify 工作原理与应用实例

Dify.AI 是一款开源的 LLM&#xff08;大语言模型&#xff09;应用开发平台。它融合了后端即服务&#xff08;Backend as a Service, BaaS&#xff09;和 LLMOps 的理念&#xff0c;允许开发者&#xff08;甚至非技术人员&#xff09;通过可视化界面快速构建生成式 AI 应用。 本…

作者头像 李华
网站建设 2026/6/16 7:00:17

VRM与VRChat模型互转终极指南:免费工具让新手快速上手

VRM与VRChat模型互转终极指南&#xff1a;免费工具让新手快速上手 【免费下载链接】VRMConverterForVRChat 项目地址: https://gitcode.com/gh_mirrors/vr/VRMConverterForVRChat 还在为VRM模型无法在VRChat中使用而烦恼吗&#xff1f;现在&#xff0c;一款强大的免费转…

作者头像 李华
网站建设 2026/6/15 17:44:41

Inventor 二次开发从入门到精通(1)

Autodesk Inventor 是一款面向机械设计的三维参数化建模软件&#xff0c;其开放的 API 体系为二次开发提供了强大的支撑。本教程围绕 Inventor 的 **.NET API&#xff08;C# 为主&#xff09;** 展开&#xff0c;兼顾 VBA、iLogic 等开发方式&#xff0c;从开发环境搭建到高级实…

作者头像 李华