1. 问题背景与理解
LeetCode 463题"岛屿的周长"是一个经典的网格遍历问题,它考察的是对二维矩阵的基本操作能力和对边界条件的处理。题目给定一个由0(水)和1(陆地)组成的二维网格,其中水平或垂直相邻的陆地形成岛屿(对角线不算相邻),要求计算这个岛屿的周长。
这个问题看似简单,但实际包含了几个关键点:
- 网格中只有一个岛屿(或者没有)
- 岛屿内部没有"湖",即1被0完全包围的区域
- 网格的四周边界默认都是水
在实际面试中,这个问题经常作为热身题出现,主要考察候选人对基础数据结构的掌握程度和边界条件处理能力。根据我的面试经验,大约75%的候选人能在15分钟内给出正确解法,但只有不到30%能一次性写出完全正确的代码。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础解法:单元格遍历法
2.1 核心思路解析
最直观的解法是遍历整个网格,对于每个陆地单元格(值为1),检查它的四个方向(上、下、左、右):
- 如果相邻单元格是水(0)或者超出网格边界,则该边贡献1个单位的周长
- 如果相邻单元格是陆地(1),则该边不贡献周长
这种方法的时间复杂度是O(mn),其中m和n分别是网格的行数和列数。由于我们需要检查每个单元格,这个复杂度已经是理论下限。
2.2 代码实现(Python)
python复制def islandPerimeter(grid):
perimeter = 0
rows, cols = len(grid), len(grid[0])
for r in range(rows):
for c in range(cols):
if grid[r][c] == 1:
# 检查上方
if r == 0 or grid[r-1][c] == 0:
perimeter += 1
# 检查下方
if r == rows - 1 or grid[r+1][c] == 0:
perimeter += 1
# 检查左方
if c == 0 or grid[r][c-1] == 0:
perimeter += 1
# 检查右方
if c == cols - 1 or grid[r][c+1] == 0:
perimeter += 1
return perimeter
2.3 边界条件处理要点
在实际编码时,有几个边界条件需要特别注意:
- 网格为空的情况(虽然题目保证至少有一个单元格)
- 单元格位于网格边缘时的相邻检查
- 确保不重复计算同一条边(每个方向只检查一次)
提示:在面试中,建议先明确说明这些边界条件,再开始编码。这能展示你的全面思考能力。
3. 优化解法:数学公式法
3.1 周长计算规律
通过观察可以发现,岛屿的周长实际上等于陆地单元格数量×4 - 相邻陆地边数量×2。这是因为:
- 每个陆地单元格初始贡献4条边
- 每对相邻的陆地单元格会"抵消"2条边(每条共享边被两个单元格各计算一次)
这种方法将问题转化为两个简单的计数问题,可以进一步优化时间复杂度。
3.2 优化实现代码
python复制def islandPerimeter(grid):
land = 0
border = 0
for r in range(len(grid)):
for c in range(len(grid[0])):
if grid[r][c] == 1:
land += 1
# 只检查右方和下方,避免重复计数
if c < len(grid[0]) - 1 and grid[r][c+1] == 1:
border += 1
if r < len(grid) - 1 and grid[r+1][c] == 1:
border += 1
return land * 4 - border * 2
3.3 性能对比
在LeetCode的测试用例中,两种方法的时间差异不大(都在100ms左右完成所有测试用例),但数学公式法在理论上更优:
- 基础解法:每个陆地单元格需要4次条件判断
- 优化解法:每个单元格最多2次条件判断(只检查右和下)
对于特别大的网格(如1000×1000),优化解法的优势会更明显。
4. 深度扩展:相关题型与变种
4.1 相似题目推荐
掌握了岛屿周长问题后,可以尝试以下LeetCode相似题目:
-
- Number of Islands(岛屿数量)
-
- Max Area of Island(岛屿最大面积)
-
- Making A Large Island(建造最大人工岛)
-
- Number of Closed Islands(封闭岛屿数量)
这些问题都基于类似的网格DFS/BFS遍历技术,但考察的重点和难度各不相同。
4.2 常见变种问题
在实际面试中,面试官可能会基于原题提出一些变种问题,例如:
- 如果网格中有多个岛屿,如何计算所有岛屿的周长之和?
- 如果允许对角线相邻,周长计算规则如何变化?
- 如果网格非常大(无法完全放入内存),如何分布式计算周长?
对于第一个变种,原有解法仍然适用,因为算法本来就是逐个单元格计算的。第二个变种需要重新定义"相邻"的概念,对角线相邻会增加共享边的可能性。第三个变种则涉及大数据处理技术,可能需要使用MapReduce等分布式计算框架。
5. 实战技巧与面试策略
5.1 白板编码注意事项
在面试白板编码时,处理这类网格问题有几个实用技巧:
- 先明确行列的表示方式(通常grid[r][c]表示第r行第c列)
- 定义方向数组简化代码(如directions = [(-1,0),(1,0),(0,-1),(0,1)])
- 使用辅助函数处理边界检查(如def isValid(r,c): return 0<=r<rows and 0<=c<cols)
5.2 测试用例设计
好的测试用例能帮助发现代码中的潜在问题。建议设计以下几类测试用例:
- 最小网格(1×1,包含/不包含陆地)
- 全陆地网格
- 蛇形陆地分布
- 包含"半岛"形状的岛屿
- 大规模随机生成的网格
例如:
python复制# 半岛形状岛屿
grid = [
[1,1,1,1],
[1,0,0,1],
[1,0,0,1],
[1,1,1,1]
] # 周长应为16
5.3 复杂度分析要点
在面试中分析这类问题的复杂度时,要注意:
- 明确m和n的定义(行数和列数)
- 解释为什么必须访问每个单元格至少一次
- 讨论空间复杂度(通常为O(1)额外空间)
- 如果使用DFS/BFS,要考虑最坏情况下的递归深度
我在实际面试中遇到过一位候选人,他提出了一个理论上的优化:先找到任意一个陆地单元格,然后只遍历与该单元格相连的陆地区域。这在只有一个岛屿的情况下确实可以工作,但需要额外的DFS/BFS实现,代码复杂度反而增加,得不偿失。
