题目重点:Google grid path 面试题详解:把路径代价定义为经过格子的最大高度,用 Dijkstra 求最小可能水位,并比较二分阈值加 BFS。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 网格非空且为矩形,从左上到右下,只能上下左右移动;每个格子的值是进入该格所需的最低水位。
- 一条路径的代价是这条路径经过的所有格子中的最大高度,包含起点与终点。目标是最小化这个最大值。
- 若题面加入障碍,需在扩展邻居时跳过;本文代码按全部格子可通行实现。
解题路线
- 令 best[r][c] 表示走到该格子时可能达到的最小“路径最高高度”。起点的值就是自身高度。
- 从当前格走到邻格时,新的路径代价是 max(当前代价, 邻格高度),若更小就更新并入最小堆。
- 该代价沿路径不会下降,因此每次弹出的最小未确定状态已经最优。第一次弹出终点即可返回。
手动走一遍
例如网格 [[0,2],[1,3]]:走右再下的最高高度为 3,走下再右也是 3,结果为 3。若把右下角改为 1,走下再右的最高高度变成 1,而走右再下仍为 2,算法返回 1。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
from heapq import heappop, heappush
def minimum_maximum_height(heights):
"""Minimize the largest cell height on a four-direction grid path."""
if not heights or not heights[0]:
raise ValueError("grid must be nonempty")
rows, cols = len(heights), len(heights[0])
if any(len(row) != cols for row in heights):
raise ValueError("grid must be rectangular")
infinity = float("inf")
best = [[infinity] * cols for _ in range(rows)]
best[0][0] = heights[0][0]
heap = [(best[0][0], 0, 0)]
while heap:
cost, row, col = heappop(heap)
if cost != best[row][col]:
continue
if (row, col) == (rows - 1, cols - 1):
return cost
for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):
nr, nc = row + dr, col + dc
if not (0 <= nr < rows and 0 <= nc < cols):
continue
candidate = max(cost, heights[nr][nc])
if candidate < best[nr][nc]:
best[nr][nc] = candidate
heappush(heap, (candidate, nr, nc))
raise ValueError("target is unreachable")
复杂度
设网格有 R×C 个格子,每格最多检查四条边;堆实现的时间为 O(RC log(RC)),best 与堆的空间为 O(RC)。也可以二分高度阈值,并用 BFS 判断该阈值下能否连通。
易错点与追问
容易失分的边界
- 把松弛公式误写成当前代价加邻格高度,就求成了路径和;写成相邻格高度差则变成另一道题。
- 起点高度也应纳入路径最大值;重复入堆的旧状态需要用 best 判定后跳过。
- 当面试官追问普通带权图最短路时,代价又改回路径权重之和,不能沿用 max 松弛。
继续深挖
- 二分答案的可行性具有单调性:阈值提高只会增加可进入格子。解释这一点比背二分模板更重要。
- 若要求输出具体路线,更新 best 时记录前驱,终点确定后反向还原路径。
