目录
给地图中的陆地和水域安排高度——多源 BFS 经典题解
题目描述
给你一个大小为 m x n
的整数矩阵 isWater
,代表一个地图:
- 如果
isWater[i][j] == 0
,格子(i, j)
是 陆地。 - 如果
isWater[i][j] == 1
,格子(i, j)
是 水域。
要求你为每个格子安排一个非负高度 height[i][j]
,满足以下规则:
- 所有水域格子的高度必须为 0。
- 任意相邻(上下左右四方向)的两个格子高度差至多为 1。
- 找到一种方案,使得地图中最高高度值尽可能大。
返回一个大小为 m x n
的高度矩阵 height
,满足上述条件。如果存在多种解,返回任意一种即可。
解题分析
这道题的核心难点在于:
- 高度从水域开始是 0,陆地高度递增但相邻格子高度差最多为 1。
- 我们要尽可能让最高的高度最大化,也就是要让离水域最远的陆地高度最大。
这其实就是计算地图中每个陆地到最近水域的最短距离问题。高度可以看作是距离水域的最短路径长度。
为什么可以这样理解?
- 水域高度是 0。
- 相邻格子高度差 ≤ 1,说明高度随着距离水域的“跳数”最多递增 1。
- 因此,陆地的高度就是其距离最近水域格子的最短路径距离。
解题方法:多源 BFS
这道题可以用经典的多源 BFS解决:
- 初始化队列:把所有水域格子作为 BFS 的起点,初始高度为 0。
- 初始化高度矩阵:将水域格子高度设为 0,其他格子设为 -1 表示未赋值。
- 广度优先搜索:
-
- 从队列中弹出格子
(x, y)
。 - 遍历其上下左右邻居
(nx, ny)
,如果邻居未被访问(高度为 -1),则将其高度设为height[x][y] + 1
,并入队。
- 从队列中弹出格子
- BFS 结束后,高度矩阵即为满足条件的解。
代码实现(Python)
from collections import deque
from typing import List
class Solution:
def highestPeak(self, isWater: List[List[int]]) -> List[List[int]]:
m, n = len(isWater), len(isWater[0])
height = [[-1] * n for _ in range(m)] # 高度矩阵,-1 表示未赋值
q = deque()
# 所有水域格子入队,赋高度 0
for i in range(m):
for j in range(n):
if isWater[i][j] == 1:
height[i][j] = 0
q.append((i, j))
# BFS 四个方向
directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]
# 多源 BFS
while q:
x, y = q.popleft()
for dx, dy in directions:
nx, ny = x + dx, y + dy
# 在界内且未访问
if 0 <= nx < m and 0 <= ny < n and height[nx][ny] == -1:
height[nx][ny] = height[x][y] + 1
q.append((nx, ny))
return height
复杂度分析
- 时间复杂度:
O(m * n)
,因为每个格子最多入队一次。 - 空间复杂度:
O(m * n)
,用于存储高度矩阵和队列。
示例说明
假设输入:
isWater = [
[0,1,0],
[0,0,0],
[0,0,1]
]
- 水域格子在
(0,1)
和(2,2)
,高度为 0。 - 从水域格子开始,邻近的陆地格子高度是 1,再远一点的是 2,依此类推。
得到输出:
height = [
[1,0,1],
[2,1,1],
[1,1,0]
]
满足:
- 水域高度均为 0。
- 相邻格子高度差最多为 1。
- 最高高度为 2,且这是该地图可能达到的最大高度。
其他思考与比较
- 为什么不能直接用单源 BFS?
如果用单源 BFS 从某个水域开始,可能无法正确反映距离其他水域的最短距离,结果不正确。
- 多源 BFS 的优势
把所有水域同时入队,保证 BFS 层数就是距离最近水域的最短距离。
- 为什么 BFS 适合求最短距离?
BFS 在无权图中遍历节点,第一次访问某节点的路径一定是最短路径。
总结
这道题是多源 BFS 的经典应用,考察对 BFS 的理解以及图的最短路径思想。只要正确初始化所有水域点入队,用 BFS 逐层扩展陆地高度,即可获得符合要求的高度分配方案。