399.除法求值

1.题目描述

给你一个变量对数组 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 由小写英文字母与数字组成

2.解题思路

由题意得:a / b = 2, b / c = 3 => a / c = 6 

我们需要一个数据结构,它能够通过father[x]找到它的父节点,并且通过value[x]找到它与父节点之间的倍数关系;这样的话,对于查询中的[x,y],如果这两个数位于同一个连通图中,且具有相同的父节点,我们就可以通过value[y] / value[x]来获取这两数的商,因为共同将其转为和根节点之间的倍数关系,两个数就可以直接相除,得到结果。

这个数据结构就是并查集,

它的add方法用于向father/value中加入x,初始时father[x]为空,value[x] = 1;

它有一个find方法用于先找到这个结点的根节点root,再依次将该结点x到根节点root之间所有的结点的value都置为与root的倍数关系,这样value[root] = 1,例如a/b = 2,==>father[b] = a, value[b] = 2,表示a,b之间有2倍的关系,但最终求b / c 时,要用value[c] / value[b],因为这里是把被除数当作根,所以结果应该取倒数

merge方法用于合并两个不相连的被除数/除数,将两个数按照倍数关系加入到father/value中

3.代码实现

/**
 * @description: 除法求值
 * 使用并查集-建立每个点与根节点的系数关系
 * 如果两个点位于同一个连通图,则可以通过一步除法操作获取答案
 * @author: lzy
 * @time: 2024/8/29 10:55
 */
class UnionFind {
    //记录每个节点的根节点
    public Map<String,String> father = null;
    //记录结点与根节点的系数
    public Map<String,Double> value = null;

    public UnionFind() {
        father = new HashMap<>();
        value = new HashMap<>();
    }

    /**
     * 新增结点
     * @param x
     */
    public void add(String x) {
        if (!father.containsKey(x)) {
            father.put(x,null);
            value.put(x,1.0);
        }
    }

    /**
     * 1.查找当前结点的根; 2.压缩当前结点到根节点路径上所有结点,使其father,value转为根节点的关系
     */
    public String find(String x) {
        String root = x;
        double base = 1.0;
        //root为x的根结点
        while (father.get(root) != null) {
            root = father.get(root);
            base *= value.get(root);
        }
        //压缩x到根路径上 每个节点与根的系数关系
        while (!x.equals(root)) {
            String originFather = father.get(x);
            father.put(x,root);
            value.put(x,value.get(x) * base);
            x = originFather;
            base /= value.get(originFather);
        }
        return root;
    }

    /**
     * merge (a,b) 2
     * 合并两个点 => 令father[b] = a, value[b] = 2
     */
    public void merge(String x, String y, double val) {
        String root_x = find(x);
        String root_y = find(y);
        if (!root_x.equals(root_y)) {
            father.put(root_y,root_x);
            value.put(root_y,val * value.get(x) / value.get(y));
        }
    }

    /**
     * 判断两个点是否联通
     */
    public boolean isConnected(String x, String y) {
        return value.containsKey(x) && value.containsKey(y) && find(x).equals(find(y));
    }
}

class Solution {
    public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
        int n = queries.size();
        double[] res = new double[n];
        Arrays.fill(res,-1.0);
        UnionFind uf = new UnionFind();
        for (int i = 0; i < equations.size(); i++) {
            String x = equations.get(i).get(0);
            String y = equations.get(i).get(1);
            uf.add(x);
            uf.add(y);
            uf.merge(x,y,values[i]);
        }
        for (int i = 0; i < n; i++) {
            String x = queries.get(i).get(0);
            String y = queries.get(i).get(1);
            if (uf.isConnected(x,y)) {
                res[i] = uf.value.get(y) / uf.value.get(x);
            }
        }
        return res;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值