leetcode
leetcode 351 ~ 400
除法求值

除法求值

难度:

标签:

题目描述

给你一个变量对数组 equations 和一个实数值数组 values 作为已知条件,其中 equations[i] = [Ai, Bi]values[i] 共同表示等式 Ai / Bi = values[i] 。每个 AiBi 是一个表示单个变量的字符串。

另有一些以数组 queries 表示的问题,其中 queries[j] = [Cj, Dj] 表示第 j 个问题,请你根据已知条件找出 Cj / Dj = ? 的结果作为答案。

返回 所有问题的答案 。如果存在某个无法确定的答案,则用 -1.0 替代这个答案。如果问题中出现了给定的已知条件中没有出现的字符串,也需要用 -1.0 替代这个答案。

注意:输入总是有效的。你可以假设除法运算中不会出现除数为 0 的情况,且不存在任何矛盾的结果。

注意:未在等式列表中出现的变量是未定义的,因此无法确定它们的答案。

 

示例 1:

输入:equations = [["a","b"],["b","c"]], values = [2.0,3.0], queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]]
输出:[6.00000,0.50000,-1.00000,1.00000,-1.00000]
解释:
条件:a / b = 2.0, b / c = 3.0
问题:a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ?
结果:[6.0, 0.5, -1.0, 1.0, -1.0 ]
注意:x 是未定义的 => -1.0

示例 2:

输入:equations = [["a","b"],["b","c"],["bc","cd"]], values = [1.5,2.5,5.0], queries = [["a","c"],["c","b"],["bc","cd"],["cd","bc"]]
输出:[3.75000,0.40000,5.00000,0.20000]

示例 3:

输入:equations = [["a","b"]], values = [0.5], queries = [["a","b"],["b","a"],["a","c"],["x","y"]]
输出:[0.50000,2.00000,-1.00000,-1.00000]

 

提示:

  • 1 <= equations.length <= 20
  • equations[i].length == 2
  • 1 <= Ai.length, Bi.length <= 5
  • values.length == equations.length
  • 0.0 < values[i] <= 20.0
  • 1 <= queries.length <= 20
  • queries[i].length == 2
  • 1 <= Cj.length, Dj.length <= 5
  • Ai, Bi, Cj, Dj 由小写英文字母与数字组成

代码结果

运行时间: 20 ms, 内存: 16.1 MB


/*
 * 思路:
 * 1. 使用并查集(Union-Find)数据结构来记录变量之间的关系。
 * 2. 我们将每个变量表示为图中的节点,边的权重为它们的比值。
 * 3. 对于每个查询,我们尝试在图中查找两个变量之间的路径,如果找不到则返回 -1.0。
 */
 
import java.util.*;
import java.util.stream.*;
 
public class Solution {
    private class UnionFind {
        private Map<String, String> parent;
        private Map<String, Double> ratio;
 
        public UnionFind() {
            parent = new HashMap<>();
            ratio = new HashMap<>();
        }
 
        public void union(String x, String y, double value) {
            parent.putIfAbsent(x, x);
            parent.putIfAbsent(y, y);
            ratio.putIfAbsent(x, 1.0);
            ratio.putIfAbsent(y, 1.0);
 
            String rootX = find(x);
            String rootY = find(y);
 
            if (!rootX.equals(rootY)) {
                parent.put(rootX, rootY);
                ratio.put(rootX, value * ratio.get(y) / ratio.get(x));
            }
        }
 
        public String find(String x) {
            if (!x.equals(parent.get(x))) {
                String originalParent = parent.get(x);
                parent.put(x, find(parent.get(x)));
                ratio.put(x, ratio.get(x) * ratio.get(originalParent));
            }
            return parent.get(x);
        }
 
        public Double isConnected(String x, String y) {
            if (!parent.containsKey(x) || !parent.containsKey(y)) {
                return -1.0;
            }
            String rootX = find(x);
            String rootY = find(y);
            if (rootX.equals(rootY)) {
                return ratio.get(x) / ratio.get(y);
            } else {
                return -1.0;
            }
        }
    }
 
    public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
        UnionFind uf = new UnionFind();
 
        IntStream.range(0, equations.size())
            .forEach(i -> uf.union(equations.get(i).get(0), equations.get(i).get(1), values[i]));
 
        return queries.stream()
            .mapToDouble(q -> uf.isConnected(q.get(0), q.get(1)))
            .toArray();
    }
}
 

解释

方法:

这个题解基于图的广度优先搜索(BFS)算法。首先构建一个图,其中节点表示变量,边表示变量之间的除法关系。对于每个等式 a/b=v,添加一条从 a 到 b 的有向边,权重为 v,同时添加一条从 b 到 a 的反向边,权重为 1/v。对于每个查询 x/y,从 x 开始进行 BFS 搜索,同时记录路径上的权重乘积,直到找到 y 或搜索完整个连通分量。最后将搜索结果存入答案数组。

时间复杂度:

O(M*N)

空间复杂度:

O(N)

代码细节讲解

🦆
在构建图的过程中,为何每个变量对都需要添加反向边,并设置权重为原权重的倒数?
在构建图的过程中,每添加一条有向边表示一个变量除以另一个变量的结果,例如 a/b = v。为了处理可能的查询,如 b/a,我们需要添加一条反向边,并且设置其权重为原权重的倒数,即 1/v。这使得无论查询是正向还是反向,我们都可以直接通过图中的路径找到答案。这种做法确保了图的完整性和对称性,使得任意两个变量之间的除法关系都可以通过图直接得到。
🦆
如何确保在BFS过程中,路径权重乘积的计算不会因浮点数精度问题而导致结果不准确?
确保BFS过程中路径权重乘积的计算精度,主要依赖于浮点数的处理方式和编程语言本身的浮点数精度。在实际应用中,可以通过限制浮点数的位数,例如使用 Python 的 decimal 模块来更精确地控制浮点数的精度。此外,对于最终结果的验证,可以通过限制输出的小数位数来减少因精度问题带来的影响。尽管完全避免浮点数误差很难,但通过这些方法可以在大多数情况下保证结果的准确性。
🦆
在BFS搜索中使用了队列,为什么选择BFS而不是DFS?BFS在这种情况下有什么优势?
BFS(广度优先搜索)相比于DFS(深度优先搜索),在此类问题中通常更适合寻找最短路径或者在图中层次较浅的地方快速找到解。由于 BFS 是逐层探索,它可以较快地找到从起点到终点的最短路径(即最少的乘法操作)。这意味着在计算变量除法时,BFS 可以更快地找到结果,而不需要遍历不必要的深层路径,这在某些情况下可以提高效率并减少计算量。
🦆
如果查询中的两个变量位于不同的连通分量,算法是如何处理这种情况?
如果查询中的两个变量位于不同的连通分量,说明这两个变量之间没有任何直接或间接的乘除关系可以被追踪。在这种情况下,BFS 搜索将无法从一个变量到达另一个变量。在算法实现中,这种情况被检测到后会直接返回 -1.0,表示查询的结果是未知或不存在。这样的设计确保了算法可以正确处理图中不连通的部分。

相关问题