中文描述:
给你一个变量对数组 equations 和一个实数值数组 values 作为已知条件,其中 equations[i] = [Ai, Bi] 和 values[i] 共同表示等式 Ai / Bi = values[i] 。每个 Ai 或 Bi 是一个表示单个变量的字符串。
另有一些以数组 queries 表示的问题,其中 queries[j] = [Cj, Dj] 表示第 j 个问题,请你根据已知条件找出 Cj / Dj = ? 的结果作为答案。
返回 所有问题的答案 。如果存在某个无法确定的答案,则用 -1.0 替代这个答案。如果问题中出现了给定的已知条件中没有出现的字符串,也需要用 -1.0 替代这个答案。
注意:输入总是有效的。你可以假设除法运算中不会出现除数为 0 的情况,且不存在任何矛盾的结果。
题目描述:
You are given an array of variable pairs equations and an array of real numbers values, where equations[i] = [Ai, Bi] and values[i] represent the equation Ai / Bi = values[i]. Each Ai or Bi is a string that represents a single variable.
You are also given some queries, where queries[j] = [Cj, Dj] represents the jth query where you must find the answer for Cj / Dj = ?.
Return the answers to all queries. If a single answer cannot be determined, return -1.0.
Note: The input is always valid. You may assume that evaluating the queries will not result in division by zero and that there is no contradiction.
Example 1:
Input: equations = [[“a”,“b”],[“b”,“c”]], values = [2.0,3.0], queries = [[“a”,“c”],[“b”,“a”],[“a”,“e”],[“a”,“a”],[“x”,“x”]]
Output: [6.00000,0.50000,-1.00000,1.00000,-1.00000]
Explanation:
Given: a / b = 2.0, b / c = 3.0
queries are: a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ?
return: [6.0, 0.5, -1.0, 1.0, -1.0 ]
Example 2:
Input: equations = [[“a”,“b”],[“b”,“c”],[“bc”,“cd”]], values = [1.5,2.5,5.0], queries = [[“a”,“c”],[“c”,“b”],[“bc”,“cd”],[“cd”,“bc”]]
Output: [3.75000,0.40000,5.00000,0.20000]
Example 3:
Input: equations = [[“a”,“b”]], values = [0.5], queries = [[“a”,“b”],[“b”,“a”],[“a”,“c”],[“x”,“y”]]
Output: [0.50000,2.00000,-1.00000,-1.00000]
Constraints:
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 consist of lower case English letters and digits.
方法1:
Time complexity:
O
(
M
N
×
α
(
M
N
)
)
O(MN×α(MN))
O(MN×α(MN)),其中 M 和 N 分别为行数和列数。注意当使用路径压缩(见 find 函数)和按秩合并(见数组 rank)实现并查集时,单次操作的时间复杂度为
α
(
M
N
)
\alpha(MN)
α(MN),其中
α
(
M
N
)
\alpha(MN)
α(MN)为反阿克曼函数,当自变量 x 的值在人类可观测的范围内(宇宙中粒子的数量)时,函数
α
(
M
N
)
\alpha(MN)
α(MN)的值不会超过 5,因此也可以看成是常数时间复杂度。
Space complexity:
O
(
M
N
)
O(MN)
O(MN)
带权并查集:
根据提供的equations和values,在一个除法中的两个数可以看成是具有“连通”关系,因为我们可以用一个数来表示另一个数 a / b = 2.0 说明 a = 2b。根据除法的传递性。只要两个除法中有相同的数,则它们就在同一个集合中。
可以将题目给出的 equation 中的两个变量所在的集合进行「合并」,同在一个集合中的两个变量就可以通过某种方式计算出它们的比值。具体来说,可以把 不同的变量的比值转换成为相同的变量的比值
作者:LeetCode
链接:https://leetcode-cn.com/problems/evaluate-division/solution/399-chu-fa-qiu-zhi-nan-du-zhong-deng-286-w45d/
在合并时我们要注意两个变量的比值。
x/y = w =>
f
a
t
h
e
r
[
x
]
=
y
father[x] = y
father[x]=y ,
w
e
i
g
h
t
s
[
x
]
=
w
weights[x] = w
weights[x]=w
x/father[x] = weights[x]
y/father[y] = weights[y]
合并时 把father[y] 设置为 root 则新的旧的根要更新权值 weights[father[x]] =>
f
a
t
h
e
r
[
x
]
/
f
a
t
h
e
r
[
y
]
=
(
x
/
w
e
i
g
h
t
s
[
x
]
)
/
(
y
/
w
e
i
g
h
t
s
[
y
]
)
=
w
∗
w
e
i
g
h
t
s
[
y
]
/
w
e
i
g
h
t
s
[
x
]
father[x]/father[y]= (x/weights[x]) / (y/weights[y])= w*weights[y]/weights[x]
father[x]/father[y]=(x/weights[x])/(y/weights[y])=w∗weights[y]/weights[x]
class Solution {
public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
int equationsSize = equations.size();
UnionFind uf = new UnionFind(2*equationsSize);
// 第 1 步:预处理,将变量的值与 id 进行映射,使得并查集的底层使用数组实现,方便编码
int id = 0;
Map<String, Integer> map = new HashMap<>(2 * equationsSize);
for (int i = 0; i < equationsSize; i++) {
List<String> equation = equations.get(i);
String s1 = equation.get(0);
String s2 = equation.get(1);
if(!map.containsKey(s1)){
map.put(s1, id);
id++;
}
if(!map.containsKey(s2)){
map.put(s2, id);
id++;
}
uf.union(map.get(s1), map.get(s2), values[i]);
}
int queriesSize = queries.size();
double[] res = new double[queriesSize];
for (int i = 0; i < queriesSize; i++) {
List<String> query = queries.get(i);
String q1 = query.get(0);
String q2 = query.get(1);
Integer id1 = map.get(q1);
Integer id2 = map.get(q2);
if (id1 == null || id2 == null) {
res[i] = -1.0d;
} else {
res[i] = uf.isConnected(id1, id2);
}
}
return res;
}
}
class UnionFind{
private int[] parent;
private double[] weights; // 指向的parent结点的权值
UnionFind(int x){
parent = new int[x];
weights = new double[x];
for(int i = 0; i < x;i++){
parent[i] = i;
weights[i] = 1.0d;
}
}
public int find(int x){
if(parent[x] != x){
int origin = parent[x];
parent[x] = find(parent[x]);
weights[x] *= weights[origin];
}
return parent[x];
}
public void union(int x, int y, double w){
int fx = find(x);
int fy = find(y);
if(fx != fy){
parent[fx] = fy;
}
weights[fx] = w*weights[y]/weights[x];
}
public double isConnected(int x, int y) {
int fx = find(x);
int fy = find(y);
if (fx == fy) {
return weights[x]/weights[y];
} else {
return -1.0d;
}
}
}