题目重点:长度与温度分属不同连通分量;用仿射边 y=ax+b 处理比例和偏移换算。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练版每条有向转换写作 y=a·x+b,a≠0;反向为 x=(y−b)/a。长度边可取 b=0,温度边可能有偏移。
- 同一连通分量内不同路径的换算结果一致;题目未明确冲突边处理规则,若允许矛盾公式,应在入图时检测并拒绝或指定优先级。
- 不存在跨维度合法换算;未知单位或不连通返回 None。浮点比较须容差处理。
解题路线
- 每条公式 source→target 添加正向边 (a,b) 和逆向边 (1/a,−b/a)。
- 查询从源单位 BFS:队列携带当前数值,经过边时执行新值=a×旧值+b;到达目标即返回。
- 源和目标在不同连通分量时遍历结束返回 None;同名未知单位也不凭空视为合法。
- 用与图遍历实现独立的 canonical unit 公式(米和摄氏度)核对 126 个查询,覆盖正反转换、零、负数与跨家族无解。
手动走一遍
长度:1 米=100 厘米、1 米=0.001 公里,250 厘米→2.5 米→0.0025 公里。温度:F=1.8C+32、K=C+273.15,32°F→0°C→273.15K。长度到温度没有路径,应返回无解。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
from collections import defaultdict, deque
class UnitConverter:
"""Directed conversion y = scale*x + offset; inverse added automatically."""
def __init__(self) -> None:
self.graph: dict[str, list[tuple[str, float, float]]] = defaultdict(list)
def add_conversion(
self, source: str, target: str, scale: float, offset: float = 0.0
) -> None:
if source == target or scale == 0:
raise ValueError("distinct units and nonzero scale required")
self.graph[source].append((target, scale, offset))
self.graph[target].append((source, 1 / scale, -offset / scale))
def convert(self, amount: float, source: str, target: str) -> float | None:
if source not in self.graph or target not in self.graph:
return None
if source == target:
return amount
queue = deque([(source, amount)])
seen = {source}
while queue:
unit, value = queue.popleft()
for neighbor, scale, offset in self.graph[unit]:
if neighbor in seen:
continue
transformed = scale * value + offset
if neighbor == target:
return transformed
seen.add(neighbor)
queue.append((neighbor, transformed))
return None
复杂度
单次查询 BFS O(V+E) 时间、O(V) 队列/visited 空间;多次查询且公式静态时可按连通分量预处理到根的仿射变换,查询 O(1),但须做环一致性检查。
易错点与追问
容易失分的边界
- 把多家族单位强行放在一棵树的同一根下。
- 只存乘法比例,无法正确表示摄氏度↔华氏度的 +32。
- 反向边直接用 −b 而非 −b/a。
- 对矛盾的环随便选一条路径,却声称结果唯一。
- 用精确浮点等号检查环一致性。
继续深挖
- 如何预处理每个单位到根的仿射变换并 O(1) 回答大量查询?
- 添加一条会闭环的新换算时如何检测数值冲突?
- 若存在汇率等随时间变化的边,查询还要带哪些参数?
- 不同物理维度如何在类型系统中防误连?
