题目

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

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

返回 所有问题的答案 。如果存在某个无法确定的答案,则用 -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 ]
示例 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 由小写英文字母与数字组成

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/evaluate-division
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

1.并查集

在并查集模板写法的基础上加上权值

四边形法则
带权并查集中:考虑x/y=vx/y=v。
结点x,y可能有多种情况,可能x==y, find(x)==x || find(y)==yx==y, find(x)==x || find(y)==y。
我们直接考虑最复杂的情况,即find(x)!=x && find(y)!=yfind(x)!!=x && find(y)!=y,这种情况是最复杂的,其他情况都是该情况的特殊情况

*leetcode 399 除法求值 - 图1

  1. /**
  2. * @param {string[][]} equations
  3. * @param {number[]} values
  4. * @param {string[][]} queries
  5. * @return {number[]}
  6. */
  7. //模板写法
  8. class UnionFind {
  9. constructor() {
  10. this.parent = {},
  11. this.value = {}
  12. }
  13. find(x) {
  14. let x_root = x
  15. let base = 1.0
  16. while (this.parent[x_root] !== -1) {
  17. x_root = this.parent[x_root]
  18. base *= this.value[x_root]
  19. }
  20. //路径压缩
  21. while (x !== x_root) {
  22. let original_father = this.parent[x]
  23. //越远倍数越大
  24. this.value[x] *= base
  25. base /= this.value[original_father]
  26. this.parent[x] = x_root
  27. x = original_father
  28. }
  29. return x_root
  30. }
  31. merge(x, y, val) {
  32. let x_root = this.find(x)
  33. let y_root = this.find(y)
  34. if (x_root !== y_root) {
  35. this.parent[x_root] = y_root
  36. //四边形法则
  37. this.value[x_root] = this.value[y] * val / this.value[x]
  38. }
  39. }
  40. add(x) {
  41. if (!this.parent[x]) {
  42. this.parent[x] = -1
  43. this.value[x] = 1.0
  44. }
  45. }
  46. }
  47. var calcEquation = function (equations, values, queries) {
  48. const uf = new UnionFind()
  49. const n = values.length
  50. const n1 = queries.length
  51. //初始化
  52. for (let i = 0; i < n; i++) {
  53. uf.add(equations[i][0])
  54. uf.add(equations[i][1])
  55. uf.merge(equations[i][0], equations[i][1], values[i])
  56. }
  57. const res = new Array(n1).fill(-1.0)
  58. for (let i = 0; i < n1; i++) {
  59. const x1 = queries[i][0]
  60. const x2 = queries[i][1]
  61. if (uf.value[x1] && uf.value[x2] && uf.find(x1) === uf.find(x2)) {
  62. res[i] = uf.value[x1] / uf.value[x2]
  63. }
  64. }
  65. return res
  66. };
  67. var calcEquation = function(equations, values, queries) {
  68. let parentMap = new Map();
  69. let valueMap = new Map();
  70. for (let i = 0; i < equations.length; i++) {
  71. if (!parentMap.has(equations[i][0])) {
  72. parentMap.set(equations[i][0], equations[i][0]);
  73. }
  74. if (!parentMap.has(equations[i][1])) {
  75. parentMap.set(equations[i][1], equations[i][1]);
  76. }
  77. if (!valueMap.has(equations[i][0])) {
  78. valueMap.set(equations[i][0], 1);
  79. }
  80. if (!valueMap.has(equations[i][1])) {
  81. valueMap.set(equations[i][1], 1);
  82. }
  83. union(parentMap, valueMap, equations[i][0], equations[i][1], values[i]);
  84. }
  85. const result = [];
  86. for (let i = 0; i < queries.length; i++) {
  87. let tmp1 = find(parentMap, valueMap, queries[i][0]);
  88. let tmp2 = find(parentMap, valueMap, queries[i][1]);
  89. if (!tmp1 || !tmp2) {
  90. result.push(-1.0);
  91. continue;
  92. }
  93. if (tmp1.index === tmp2.index) {
  94. result.push(tmp1.value / tmp2.value);
  95. }
  96. else {
  97. result.push(-1.0);
  98. }
  99. }
  100. return result;
  101. };
  102. function union(parentMap, valueMap, index1, index2, value) {
  103. let tmp1 = find(parentMap, valueMap, index1);
  104. let tmp2 = find(parentMap, valueMap, index2);
  105. parentMap.set(tmp1.index, tmp2.index);
  106. valueMap.set(tmp1.index, (value * tmp2.value) / tmp1.value);
  107. }
  108. function find(parentMap, valueMap, index) {
  109. let value = 1;
  110. while (parentMap.get(index) && parentMap.get(index) !== index) {
  111. value *= valueMap.get(index);
  112. index = parentMap.get(index);
  113. }
  114. if (!parentMap.get(index)) {
  115. return null;
  116. }
  117. return {
  118. index,
  119. value
  120. };
  121. }

2.DFS

// 先构造图再dfs
var calcEquation = function(equations, values, queries) {
    const graph = {}, visited = new Set();

    // 构造图,equations的第一项除以第二项等于value里的对应值,第二项除以第一项等于其倒数
    for(let [index, value] of values.entries()) {
        const x = equations[index][0], y = equations[index][1];
        if(graph[x]) {
            graph[x][y] = value;
        } else {
            graph[x] = {[y]: value};
        }

        if(graph[y]) {
            graph[y][x] = 1 / value;
        } else {
            graph[y] = {[x]: 1 / value};
        }
    }

    // dfs找寻从s到t的路径并返回结果叠乘后的边权重即结果
    const dfs = (dividend, divisor) => {
        if(!graph[dividend]) {
            return -1;
        }
        if(dividend === divisor) {
            return 1;
        }
        for(let node of Object.keys(graph[dividend])) {
            if(node === divisor) {
                return graph[dividend][node];
            } else if(!visited.has(node)) {
                visited.add(node); // 添加到已访问避免重复遍历
                const value = dfs(node, divisor);
                visited.delete(node);
                if(value !== -1) {
                    return graph[dividend][node] * value;
                }
            }
        }
        return -1;
    }

    return queries.map(([dividend, divisor]) => dfs(dividend, divisor));
};