文章目录
摘要
这道题其实挺有意思的,它不是让你去做真正的除法,而是让你去"推理"不同变量之间的关系。
比如说:
- 我知道
a / b = 2.0
- 又知道
b / c = 3.0
那我就能推出a / c = 6.0
这道题的本质,其实是一个 图(Graph)问题 :
每个变量是一个节点,a / b = 2.0
代表 a -> b
的一条有向边,权重是 2.0;反过来 b -> a
也是一条边,权重是 1/2.0。
当我们要回答类似 a / c
这种问题时,本质就是在问:
"从 a 出发到 c 的路径上,所有权重连乘是多少?"
听起来抽象?别急,我们慢慢拆开看。

描述
题目输入包括三部分:
- 等式 equations :如
[["a","b"],["b","c"]]
- 对应的值 values :如
[2.0, 3.0]
,表示a / b = 2.0
、b / c = 3.0
- 查询 queries :如
[["a","c"],["b","a"],["a","e"]]
要求我们输出每个查询的结果,比如上面例子中:
a / c = 6.0
b / a = 0.5
a / e = -1.0
(因为 e 未定义)
题解答案
核心思路其实有两种常见方案:
-
图 + DFS(深度优先搜索)
- 建立一个加权有向图;
- 对每个查询执行一次 DFS;
- 在路径中不断累乘权重;
- 如果能从起点走到终点,就返回结果,否则返回
-1.0
。
-
并查集(Union-Find with Weight)
- 把每个变量放进并查集;
- 记录它与父节点之间的权重;
- 查询时通过比较"根节点"是否相同来判断是否连通;
- 并根据权重关系算出最终比值。
在 Swift 里,用 DFS 实现会更直观,也方便演示。下面我们就用 DFS 的方法写一个可运行的版本。

题解代码分析
下面是完整的 Swift 实现版本:
swift
import Foundation
class Solution {
private var graph = [String: [(String, Double)]]()
init() {}
// 主函数
func calcEquation(_ equations: [[String]], _ values: [Double], _ queries: [[String]]) -> [Double] {
buildGraph(equations, values)
var results: [Double] = []
for query in queries {
let (start, end) = (query[0], query[1])
var visited = Set<String>()
let result = dfs(start, end, 1.0, &visited)
results.append(result)
}
return results
}
// 建图
private func buildGraph(_ equations: [[String]], _ values: [Double]) {
for (i, eq) in equations.enumerated() {
let (a, b) = (eq[0], eq[1])
let val = values[i]
graph[a, default: []].append((b, val))
graph[b, default: []].append((a, 1.0 / val))
}
}
// DFS 搜索路径
private func dfs(_ start: String, _ end: String, _ value: Double, _ visited: inout Set<String>) -> Double {
guard let neighbors = graph[start] else {
return -1.0
}
if start == end { return value }
visited.insert(start)
for (next, weight) in neighbors {
if !visited.contains(next) {
let result = dfs(next, end, value * weight, &visited)
if result != -1.0 {
return result
}
}
}
return -1.0
}
}
代码拆解讲解
1. 建图阶段
我们把每个等式看成两条有向边:
a -> b
权重为val
b -> a
权重为1 / val
例如:
txt
a / b = 2.0
b / c = 3.0
构建出的图是:
txt
a -> b (2.0)
b -> a (0.5)
b -> c (3.0)
c -> b (0.333)
2. DFS 搜索阶段
DFS 的任务就是找一条从 start
到 end
的路径,并在沿途不断乘上边权。
举个例子:
- 要算
a / c
; - 从
a
出发 → 找到邻居b
(权重 2.0); - 继续从
b
出发 → 找到邻居c
(权重 3.0); - 最终结果是
2.0 * 3.0 = 6.0
。
如果某个节点没有通往目标的路径,就返回 -1.0
。
3. visited 集合
用来防止死循环,比如 a / b
、b / a
互相指向时,DFS 可能无限递归。
示例测试及结果
我们用题目中的例子来跑一下看看。
swift
let equations = [["a","b"],["b","c"]]
let values = [2.0, 3.0]
let queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]]
let sol = Solution()
let result = sol.calcEquation(equations, values, queries)
print(result)
输出结果:
txt
[6.0, 0.5, -1.0, 1.0, -1.0]
非常符合预期。
你可以再试一个:
swift
let eq2 = [["a","b"],["b","c"],["bc","cd"]]
let val2 = [1.5, 2.5, 5.0]
let q2 = [["a","c"],["c","b"],["bc","cd"],["cd","bc"]]
let sol2 = Solution()
print(sol2.calcEquation(eq2, val2, q2))
输出:
txt
[3.75, 0.4, 5.0, 0.2]
完美通过
时间复杂度
- 建图阶段: O(N),其中 N 是等式数量;
- 每次查询: 在最坏情况下 DFS 会遍历所有节点,复杂度 O(V + E),但因为题目规模小(最多 20),可视为常数;
- 总体复杂度: O(N + Q × (V + E)),对这道题来说性能非常充足。
空间复杂度
- 图存储结构使用 O(V + E);
- DFS 递归栈深度 O(V);
- 所以总体空间复杂度是 O(V + E)。
总结
这道题虽然看起来是"除法",但核心完全是图的遍历。
可以把它理解成一个"变量之间的依赖网络",我们只需要在这个网络里找到一条从起点到终点的路径,并把沿途的"权重"连乘起来。
总结一下两种解法的优缺点:
方法 | 思路 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
DFS/BFS | 图遍历 | 简单直观 | 查询多时会重复计算 | 小规模题或快速实现 |
并查集 | 动态连通性 | 查询快 | 实现复杂 | 查询频繁或动态更新场景 |
实际工程中的应用类比:
- 货币兑换系统:每种货币相当于一个节点,汇率就是边权;
- 单位换算系统 :比如
km/m
、cm/mm
的换算; - 分布式依赖图:不同微服务之间的调用链条计算。
如果你能透彻理解这道题,基本上已经掌握了「图建模 + 路径搜索 + 权重传递」的通用套路,这在算法与工程中都极其常见。