岛屿的周长 图解题解
这道题到底在问什么
- 输入
- grid = [[0,1,0,0],[1,1,1,0],[0,1,0,0],[1,1,0,0]]
- 输出
- 16
先想最直接的笨办法
扫到第 1 块陆地 (0,1)。挨个看它四周:上出界、下陆、左水、右陆。其中露在外面(挨水或出界)的有 2 条边。(动画第 5 步)
最优解:一步一步想明白
- 3核心就一句话:一块陆地起步算 4 条边,它每挨着一块陆地就少一条边。逐格扫、逐边数,加起来就是周长。
- 4陆地=1,水=0 · 周长从 0 起这张 5×5 网格里,深色是陆地(1)、浅色是水(0)。我们从左上角一行一行往下扫,周长从 0 开始累加。
- 5四周情况:上出界 下陆 左水 右陆扫到第 1 块陆地 (0,1)。挨个看它四周:上出界、下陆、左水、右陆。其中露在外面(挨水或出界)的有 2 条边。
- 6本格 +2 条,累计周长 2把这 2 条外露边加进周长,累计变成 2。这块陆地数完,标记为已处理(变灰),继续扫下一块。
- 7四周情况:上出界 下陆 左陆 右水扫到第 2 块陆地 (0,2)。挨个看它四周:上出界、下陆、左陆、右水。其中露在外面(挨水或出界)的有 2 条边。
- 8本格 +2 条,累计周长 4把这 2 条外露边加进周长,累计变成 4。这块陆地数完,标记为已处理(变灰),继续扫下一块。
- 9四周情况:上陆 下水 左水 右陆扫到第 3 块陆地 (1,1)。挨个看它四周:上陆、下水、左水、右陆。其中露在外面(挨水或出界)的有 2 条边。
- 10本格 +2 条,累计周长 6把这 2 条外露边加进周长,累计变成 6。这块陆地数完,标记为已处理(变灰),继续扫下一块。
- 11四周情况:上陆 下陆 左陆 右陆扫到第 4 块陆地 (1,2)。挨个看它四周:上陆、下陆、左陆、右陆。其中露在外面(挨水或出界)的有 0 条边。
- 12本格 +0 条,累计周长 6把这 0 条外露边加进周长,累计变成 6。这块陆地数完,标记为已处理(变灰),继续扫下一块。
- 13四周情况:上水 下水 左陆 右水扫到第 5 块陆地 (1,3)。挨个看它四周:上水、下水、左陆、右水。其中露在外面(挨水或出界)的有 3 条边。
- 14本格 +3 条,累计周长 9把这 3 条外露边加进周长,累计变成 9。这块陆地数完,标记为已处理(变灰),继续扫下一块。
- 15四周情况:上陆 下陆 左水 右水扫到第 6 块陆地 (2,2)。挨个看它四周:上陆、下陆、左水、右水。其中露在外面(挨水或出界)的有 2 条边。
- 16本格 +2 条,累计周长 11把这 2 条外露边加进周长,累计变成 11。这块陆地数完,标记为已处理(变灰),继续扫下一块。
- 17四周情况:上水 下陆 左水 右陆扫到第 7 块陆地 (3,1)。挨个看它四周:上水、下陆、左水、右陆。其中露在外面(挨水或出界)的有 2 条边。
- 18本格 +2 条,累计周长 13把这 2 条外露边加进周长,累计变成 13。这块陆地数完,标记为已处理(变灰),继续扫下一块。
- 19四周情况:上陆 下水 左陆 右水扫到第 8 块陆地 (3,2)。挨个看它四周:上陆、下水、左陆、右水。其中露在外面(挨水或出界)的有 2 条边。
- 20本格 +2 条,累计周长 15把这 2 条外露边加进周长,累计变成 15。这块陆地数完,标记为已处理(变灰),继续扫下一块。
- 21四周情况:上陆 下出界 左水 右水扫到第 9 块陆地 (4,1)。挨个看它四周:上陆、下出界、左水、右水。其中露在外面(挨水或出界)的有 3 条边。
- 22本格 +3 条,累计周长 18把这 3 条外露边加进周长,累计变成 18。这块陆地数完,标记为已处理(变灰),继续扫下一块。
- 23所有陆地数完,周长 = 18每块陆地的外露边都数完了,全部加起来周长 = 18,这就是答案。
⚠️ 容易写错的地方
✗ 错:忘了网格外的边界也算周长
✓ 对:陆地格在边缘时,越界的方向也要算露出的边
靠网格边的陆地,那条朝外的边没有水也没有陆地接着,它就是岛的外圈
✗ 错:把相邻陆地的共享边只减了一次
✓ 对:两块相邻陆地共享一条边,周长要减 2(各减 1)
那一条边被两块陆地同时遮住,从两块的 4 条边里各扣掉一条
✗ 错:对水格子也去数边
✓ 对:只在陆地(grid[i][j]==1)处累加
水不属于岛,给水数边会算出毫无意义的结果
完整代码(Python / C++ / Java)
Python
def islandPerimeter(grid):
R, C = len(grid), len(grid[0])
per = 0
for i in range(R):
for j in range(C):
if grid[i][j] == 1: # 只看陆地
per += 4 # 起步算 4 条边
if i > 0 and grid[i-1][j] == 1: # 上邻是陆地
per -= 2 # 共享边, 两块各减 1
if j > 0 and grid[i][j-1] == 1: # 左邻是陆地
per -= 2
return perC++
int islandPerimeter(vector<vector<int>>& g){
int R=g.size(), C=g[0].size(), per=0;
for(int i=0;i<R;i++)for(int j=0;j<C;j++)
if(g[i][j]==1){
per += 4;
if(i>0 && g[i-1][j]==1) per -= 2;
if(j>0 && g[i][j-1]==1) per -= 2;
}
return per;
}Java
public int islandPerimeter(int[][] g) {
int R=g.length, C=g[0].length, per=0;
for(int i=0;i<R;i++) for(int j=0;j<C;j++)
if(g[i][j]==1){
per += 4;
if(i>0 && g[i-1][j]==1) per -= 2;
if(j>0 && g[i][j-1]==1) per -= 2;
}
return per;
}复杂度
时间
O(R×C)
把网格每个格子扫一遍,每格只看常数条边
空间
O(1)
只用一个计数器累加周长,不开额外网格
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 岛屿的周长 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
为什么一块陆地的贡献是「4 减相邻陆地个数」?+
陆地是个正方形,本来有 4 条边都可能露在外面。但每挨着一块相邻陆地,就有一条边被那块陆地遮住、变成岛内部的边,不再算周长。所以露出的边数 = 4 − 相邻陆地个数。
代码里为什么只检查上邻和左邻,还减 2?+
从上到下、从左到右扫的时候,每对相邻陆地会在「右下那块」被处理时碰到它的上邻或左邻。一条共享边由两块陆地分摊,给两块各减 1、合起来减 2,就不会把同一条边数两遍。
如果岛里有湖(被陆地围住的水),这个解法还对吗?+
本题保证没有湖,所以解法直接成立。即便有湖,按「数每条挨水的边」的思路同样能算对,因为湖边的陆地那条朝湖的边照样会被数到。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 岛屿的周长 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
把这道题真正拿下
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。