题目及测试
package pid399;
/* 399. 除法求值
给出方程式 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, vector& values, vector> queries(方程式,方程式结果,问题方程式), 其中 equations.size() == values.size(),即方程式的长度与方程式结果长度相等(程式与结果一一对应),并且结果值均为正数。以上为方程式的描述。 返回vector类型。
基于上述例子,输入如下:
equations(方程式) = [ ["a", "b"], ["b", "c"] ],
values(方程式结果) = [2.0, 3.0],
queries(问题方程式) = [ ["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"] ].
输入总是有效的。你可以假设除法运算中不会出现除数为0的情况,且不存在任何矛盾的结果。
*/
import java.util.ArrayList;
import java.util.List;
public class main {
public static void main(String[] args) {
List list = null;
List> ito1 = new ArrayList>();
list = new ArrayList<>();
list.add("a");
list.add("b");
ito1.add(list);
list = new ArrayList<>();
list.add("b");
list.add("c");
ito1.add(list);
double[] ito2 = new double[]{2,3};
List> ito3 = new ArrayList>();
list = new ArrayList<>();
list.add("a");
list.add("c");
ito3.add(list);
list = new ArrayList<>();
list.add("b");
list.add("a");
ito3.add(list);
list = new ArrayList<>();
list.add("a");
list.add("e");
ito3.add(list);
list = new ArrayList<>();
list.add("a");
list.add("a");
ito3.add(list);
list = new ArrayList<>();
list.add("x");
list.add("x");
ito3.add(list);
}
private static void test(List> ito1, double[] ito2, List> ito3) {
Solution solution = new Solution();
double[] rtn;
long begin = System.currentTimeMillis();
System.out.println("ito1=");
System.out.println();
for(int i=0;i
没想出来
解法1(别人的)
首先如果有a/b = 3,我们令parent[a] = b,即让b成为a的父亲,同时让val[a] = 3。
所以此处的定义非常重要:即val[]数组存放的是节点与他直接父节点的关系
因此我们在遍历equations时,根据from/to = val 的关系,要初始化一些指向和val数组的值。
但是在from已经存在(即parent里存在这个key),说明from已经在某个集合中(作为根或非根),我们为了方便起见(不需要合并集合),让不在集合的to指向from,val也取倒数。
最关键的一步在于,如果from和to在两个不同的集合内怎么办?
这时候注意看我们的find函数,find函数除了返回根以外,还进行了路径压缩以及val值更新,我们利用回溯对val值进行了更新,使得val值实际上变成了节点与根的关系,而且每个非根节点还会直接指向根节点。(路径压缩也保证了多次find操作不会使得val值被多次错误更新,因为根节点的val值是1)
也就是说如果一个集合有a/b = 3, b/c = 3,其中c作为根节点。那么在find返回之后,我们不仅拿到了c,还使得集合的形状变为了:
a -> c,val[a] = 9
b -> c, val[b] = 3
c -> c, val[c] = 1
那么回到我们from和to在两个不同集合的问题,我们发现在判断from和to是否位于一个集合中的时候,我们分别对from和to各调用了一次find操作,这说明什么?
说明此时from和to已经直接指向他们各自的根节点,并且val[from]和val[to]直接存放他们和各自根节点的比值
现在我们要对两个集合进行合并,假设from的根为root1, to的根为root2,我们让parent[root1] = root2
注意,这时候val[root1]不能再为1了,那么我们要更新为多少呢?
其实我们发现val[root1] 就是 root1 / root2的值
而且我们已有from/root1, to/root2, from/to的值
简单的数学:root1/root2 = to/root2 * from/to ÷ from/root1也就是val[root1] = val[to] * from/to ÷ val[from]
好了此时我们的val[root1]已经是正确的值了,它确实反映了root1和root2的正确关系。至于原本在root2为根的集合的节点,它们是不用发生任何变化的,这点很显然。
那么原本在root1集合中的点呢?
我们惊奇的发现,由于val[root1]已经不再是1了,因此在下一次find()操作中,其他节点会同样经过回溯更新val值,使得val值最终成为该节点与新根节点root2的比值。
非常完美!而且由于我们在query阶段,绝对会对from和to各自调用一次find操作,确保了这之后val[from]和val[to]都是必定正确的,也就是他们都真实反映了他们和各自根节点的比值。
因此如果from和to位于同一集合(假设根为root),也就是我们根据val能得到绝对正确的 from / root 和 to / root,那么 from / to等于多少呢?
这下问题就变成了小学数学了,拿下!
class Solution {
static Map parents;
static Map val;
public String find(String x) {
if (!x.equals(parents.get(x))) {
String tmpParent = parents.get(x);
String root = find(tmpParent);
double oldVal = val.get(x);
val.put(x, oldVal * val.get(tmpParent));
parents.put(x, root);
}
return parents.get(x);
}
public double[] calcEquation(List> equations, double[] values, List> queries) {
parents = new HashMap<>();
val = new HashMap<>();
int i = 0;
for (List equation : equations) {
String from = equation.get(0);
String to = equation.get(1);
double cur = values[i];
if (!parents.containsKey(from) && !parents.containsKey(to)) {
parents.put(from, to);
val.put(from, cur);
parents.put(to, to);
val.put(to, 1.0);
} else if (!parents.containsKey(from)) {
parents.put(from, to);
val.put(from, cur);
} else if (!parents.containsKey(to)) {
parents.put(to, from);
val.put(to, 1 / cur);
} else {
String pa = find(from);
String pb = find(to);
if (!pa.equals(pb)) {
parents.put(pa, pb);
val.put(pa, cur * val.get(to) / val.get(from));
}
}
i++;
}
i = 0;
double[] res = new double[queries.size()];
for (List query : queries) {
String from = query.get(0);
String to = query.get(1);
if (!parents.containsKey(from) || !parents.containsKey(to)) {
res[i++] = -1;
continue;
}
String pa = find(from);
String pb = find(to);
if (!pa.equals(pb)) res[i] = -1;
else {
res[i] = val.get(from) / val.get(to);
}
i++;
}
return res;
}
}