给出方程式 A / B = k, 其中 A 和 B 均为用字符串表示的变量, k 是一个浮点型数字。根据已知方程式求解问题,并返回计算结果。如果结果不存在,则返回 -1.0。
示例 :
给定 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 ]
输入为: vector
基于上述例子,输入如下:
equations(方程式) = [ ["a", "b"], ["b", "c"] ],
values(方程式结果) = [2.0, 3.0],
queries(问题方程式) = [ ["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"] ].
输入总是有效的。你可以假设除法运算中不会出现除数为0的情况,且不存在任何矛盾的结果。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/evaluate-division
AC代码
Map>> map;
/**
* 计算结果
* @param equations 方程式
* @param values 方程式结果
* @param queries 问题方程式
* @return 计算结果
*/
public double[] calcEquation(List> equations, double[] values, List> queries) {
// 注: a ÷ b = c中,a为被除数,b为除数,c为商
// map的key为除数,value为Set
// pair中,key为被除数,value为商
// 比如a÷b=2, b÷c=3,则map的内容为:
// map.put(b, [])
// map.put(c, [, ])
map = new HashMap<>(equations.size());
// 存所有的被除数和除数
Set allNum = new HashSet<>(equations.size());
for (int i = 0; i < equations.size(); i++) {
List equation = equations.get(i);
// 被除数
String dividend = equation.get(0);
// 除数
String divisor = equation.get(1);
// 构造map
buildMap(dividend, divisor, values[i]);
// 交换被除数和除数,再次构造map
buildMap(divisor, dividend, 1 / values[i]);
allNum.add(dividend);
allNum.add(divisor);
}
// 结果集
double[] results = new double[queries.size()];
// 结果不存在,则返回 -1.0
double noResult = -1.0;
// 遍历问题方程式
for (int i = 0; i < queries.size(); i++) {
List query = queries.get(i);
String dividend = query.get(0);
String divisor = query.get(1);
// 被除数或除数在equations中不曾出现过
if (!(allNum.contains(dividend) && allNum.contains(divisor))) {
results[i] = noResult;
continue;
}
// 被除数与除数相等
if (Objects.equals(dividend, divisor)) {
results[i] = 1;
continue;
}
Double result = calculate(dividend, divisor);
results[i] = null == result ? noResult : result;
}
return results;
}
/**
* 构造map
* @param dividend 被除数
* @param divisor 除数
* @value value 商
*/
private void buildMap(String dividend, String divisor, Double value) {
// 被除数等于除数,或者map中已存在,退出方法
if (dividend.equals(divisor) || exist(dividend, divisor)) {
return;
}
// key为被除数,value为商
Pair pair = new Pair(dividend, value);
Set> pairs = null;
if (map.containsKey(divisor)) {
pairs = map.get(divisor);
pairs.add(pair);
} else {
pairs = new HashSet<>();
pairs.add(pair);
map.put(divisor, pairs);
}
List> triples = new ArrayList<>();
// 如果被除数有被除数
if (map.containsKey(dividend)) {
// 被除数的被除数集合
Set> pairsForDividend = map.get(dividend);
for (Pair pairForDividend: pairsForDividend) {
String pairKey = pairForDividend.getKey();
Double pairValue = pairForDividend.getValue();
pairs.add(new Pair(pairKey, pairValue * value));
if (!exist(divisor, pairKey)) {
triples.add(new Triple(divisor, pairKey, 1 / (pairValue * value)));
}
}
}
// 直接在for循环里递归buildMap可能有并发修改map的风险,因此提到外面
// 交换被除数和除数,再次构造map
for (Triple triple: triples) {
buildMap(triple.left, triple.middle, triple.right);
}
}
/**
* 根据被除数和除数计算结果
* @param dividend 被除数
* @param divisor 除数
* @return 计算结果
*/
private Double calculate(String dividend, String divisor) {
Set> pairs = map.get(divisor);
if (null == pairs) {
return null;
}
for (Pair pair: pairs) {
if (pair.getKey().equals(dividend)) {
return pair.getValue();
}
}
return null;
}
/**
* 判断map中是否已存在重复数据
* 注意: 比较被除数和除数即可,不用比较商,会有浮点数精度问题
* @param dividend 被除数
* @param divisor 除数
* @return 是否已存在
*/
private boolean exist(String dividend, String divisor) {
Set> pairs = map.get(divisor);
if (null == pairs) {
return false;
}
for (Pair pair: pairs) {
if (pair.getKey().equals(dividend)) {
return true;
}
}
return false;
}
class Triple {
private L left;
private M middle;
private R right;
public Triple(L left, M middle, R right) {
this.left = left;
this.middle = middle;
this.right = right;
}
}
另一个思路
以a / b = 2, b / c = 3为例,步骤如下:
1.拿到第一个方程式时,把除数初始化为1,即把b初始化为1,然后把a初始化为2
2.维护一个队列queue,把a和b放进队列
3.while(!queue.isEmpty()),while循环中,每次拿出queue的一个值x,遍历所有方程式,如果可以利用x算出另一个变量,就把新算出的变量放进queue
4.重复步骤3,直到while退出,即所有可求出值的变量都使用过