Featured image of post 399. 除法求值

399. 除法求值

题目描述

给你一个变量对数组 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 的情况,且不存在任何矛盾的结果。

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

示例 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 由小写英文字母与数字组成

解法一:图 + DFS

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
func calcEquation(equations [][]string, values []float64, queries [][]string) []float64 {
    m := make(map[string]int)
    // 1 <= equations.length <= 20
    maxNumOfVar := 41
    // 0.0 < values[i] <= 20.0
    edges := make([][]float64, maxNumOfVar)
    for i := 0; i < maxNumOfVar; i++ {
        edges[i] = make([]float64, maxNumOfVar)
    }
    // id 从 1 开始
    id := 1
    for i, equation := range equations {
        a, b := equation[0], equation[1]
        if _, has := m[a]; !has {
            m[a] = id
            id++
        }
        if _, has := m[b]; !has {
            m[b] = id
            id++
        }
        edges[m[a]][m[b]] = values[i]
        edges[m[b]][m[a]] = 1 / values[i]
    }
    visited := make([]bool, maxNumOfVar)
    var dfs func(start, end int) float64
    dfs = func(start, end int) float64 {
        if start == end {
            return 1.0
        }
        visited[start] = true
        defer func() { visited[start] = false }()
        for next := 0; next < maxNumOfVar; next++ {
            if edges[start][next] != 0.0 && !visited[next] {
                tmp := dfs(next, end)
                if tmp > 0 {
                    return edges[start][next] * tmp
                }
            }
        }
        return -1.0
    }
    ans := make([]float64, len(queries))
    for i, query := range queries {
        ans[i] = -1.0
        c, d := query[0], query[1]
        // 由于 id 从 1 开始,故当 m[c] == 0 时不会误判。
        start, end := m[c], m[d]
        if start > 0 && end > 0 {
            tmp := dfs(start, end)
            if tmp > 0 {
                ans[i] = tmp
            }
        }
    }
    return ans
}

解法二:并查集

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
type DisjointSet struct {
    parent map[string]string
    weight map[string]float64
}

func (ds *DisjointSet) Init(relations [][]string, values []float64) {
    ds.parent = make(map[string]string)
    ds.weight = make(map[string]float64)
    for i, relation := range relations {
        a, b := relation[0], relation[1]
        if _, has := ds.weight[a]; !has {
            ds.parent[a] = a
            ds.weight[a] = 1.0
        }
        if _, has := ds.weight[b]; !has {
            ds.parent[b] = b
            ds.weight[b] = 1.0
        }
        aW, aF := ds.Find(a)
        bW, bF := ds.Find(b)
        if aF == bF {
            // a 的根和 b 的根相等
            continue
        } else {
            //   aRoot <----(y)---- bRoot
            //     ↑                 ↑
            //     aW                bW
            //     |                 |
            //     a --(values[i])-> b
            // 根据上图推导出 y = (aW) / (values[i]*bW)
            y := aW / (values[i] * bW)
            ds.parent[bF] = aF
            ds.weight[bF] = y
        }
    }
}

func (ds *DisjointSet) Find(varName string) (float64, string) {
    f := ds.parent[varName]
    if f == "" {
        return -1.0, ""
    } else if f == varName {
        return 1.0, varName
    } else {
        // 路径压缩
        weight, f := ds.Find(f)
        ds.weight[varName] = ds.weight[varName] * weight
        ds.parent[varName] = f
        return ds.weight[varName], f
    }
}

func calcEquation(equations [][]string, values []float64, queries [][]string) []float64 {
    ds := &DisjointSet{}
    ds.Init(equations, values)
    ans := make([]float64, len(queries))
    for i, query := range queries {
        ans[i] = -1.0
        c, d := query[0], query[1]
        cW, cF := ds.Find(c)
        dW, dF := ds.Find(d)
        if cF == dF && cF != "" {
            ans[i] = cW / dW
        }
    }
    return ans
}

官方题解如下:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Solution {

    public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
        int equationsSize = equations.size();

        UnionFind unionFind = new UnionFind(2 * equationsSize);
        // 第 1 步:预处理,将变量的值与 id 进行映射,使得并查集的底层使用数组实现,方便编码
        Map<String, Integer> hashMap = new HashMap<>(2 * equationsSize);
        int id = 0;
        for (int i = 0; i < equationsSize; i++) {
            List<String> equation = equations.get(i);
            String var1 = equation.get(0);
            String var2 = equation.get(1);

            if (!hashMap.containsKey(var1)) {
                hashMap.put(var1, id);
                id++;
            }
            if (!hashMap.containsKey(var2)) {
                hashMap.put(var2, id);
                id++;
            }
            unionFind.union(hashMap.get(var1), hashMap.get(var2), values[i]);
        }

        // 第 2 步:做查询
        int queriesSize = queries.size();
        double[] res = new double[queriesSize];
        for (int i = 0; i < queriesSize; i++) {
            String var1 = queries.get(i).get(0);
            String var2 = queries.get(i).get(1);

            Integer id1 = hashMap.get(var1);
            Integer id2 = hashMap.get(var2);

            if (id1 == null || id2 == null) {
                res[i] = -1.0d;
            } else {
                res[i] = unionFind.isConnected(id1, id2);
            }
        }
        return res;
    }

    private class UnionFind {

        private int[] parent;

        /**
         * 指向的父结点的权值
         */
        private double[] weight;


        public UnionFind(int n) {
            this.parent = new int[n];
            this.weight = new double[n];
            for (int i = 0; i < n; i++) {
                parent[i] = i;
                weight[i] = 1.0d;
            }
        }

        public void union(int x, int y, double value) {
            int rootX = find(x);
            int rootY = find(y);
            if (rootX == rootY) {
                return;
            }

            parent[rootX] = rootY;
          	// 关系式的推导请见「参考代码」下方的示意图
            weight[rootX] = weight[y] * value / weight[x];
        }

        /**
         * 路径压缩
         *
         * @param x
         * @return 根结点的 id
         */
        public int find(int x) {
            if (x != parent[x]) {
                int origin = parent[x];
                parent[x] = find(parent[x]);
                weight[x] *= weight[origin];
            }
            return parent[x];
        }

        public double isConnected(int x, int y) {
            int rootX = find(x);
            int rootY = find(y);
            if (rootX == rootY) {
                return weight[x] / weight[y];
            } else {
                return -1.0d;
            }
        }
    }
}
Licensed under CC BY-NC-SA 4.0
最后更新于 2023/08/07 21:42:09
comments powered by Disqus
Built with Hugo
主题 StackJimmy 设计