Leetcode 399: 除法求值

该博客详细介绍了LeetCode 399题目的解法,涉及利用带权并查集解决变量之间的除法规则。通过对给定的变量对和值数组,计算查询数组中每个问题的Cj / Dj答案。文章讨论了时间复杂度和空间复杂度,并给出了具体的解题思路和步骤。
摘要由CSDN通过智能技术生成

中文描述:

给你一个变量对数组 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])=wweights[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;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值