leetcode 399. Evaluate Division(除法求值) [带权并查集]

给出方程式 A / B = k, 其中 A 和 B 均为代表字符串的变量, k 是一个浮点型数字。根据已知方程式求解问题,并返回计算结果。如果结果不存在,则返回 -1.0。

示例 :
给定 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 ]

输入为: vector> equations, vector& values, vector> queries(方程式,方程式结果,问题方程式), 其中 equations.size() == values.size(),即方程式的长度与方程式结果长度相等(程式与结果一一对应),并且结果值均为正数。以上为方程式的描述。 返回vector类型。

基于上述例子,输入如下:

equations(方程式) = [ [“a”, “b”], [“b”, “c”] ],
values(方程式结果) = [2.0, 3.0],
queries(问题方程式) = [ [“a”, “c”], [“b”, “a”], [“a”, “e”], [“a”, “a”], [“x”, “x”] ].
输入总是有效的。你可以假设除法运算中不会出现除数为0的情况,且不存在任何矛盾的结果。


题解: 首先可以很容易想到,给出的关系式可以用于建图,变量名即节点,关系式结果即两变量名的权值.并且为有向边,如a / b = 2.0即 a–>b,边权为2.0,而b–>a边权为0.5.

并且这样的关系具有传递性如 a—2.0—>b—3.0—>c,那么a---->c的权值即2.0*3.0=6.0

相反,c—>a 的权值同样为1/6,即a—>c的倒数.

在这个基础上,我们可以构造一棵树,或者更准确的说是一个DAG(有向无环图),你可能会问,为什么是树不是图,并且这个关系式的构造方式明显可以形成环.
可以这样想,根据给出的关系式,已经确定了两者关系是 [前者 / 后者 = 权值] 那么在建图时就一定要建立 前者 —> 后者 ,val=权值 的边吗?答案是否定的,我们完全可以根据 原式逆向建单项边 后者 —> 前者 ,val=1/权值

如此以来建图的方式就很灵活了,这样构造出DAG是完全没问题的.

再者,我们将其抽象成一棵树,从存在相互关系的变量集合中任选一个变量作为根节点. 这样有一个好处,从根节点到达树上任意节点的结果都可以传递性预处理出来,也就是可以将路径压缩. 而树上任意两个节点A、B的关系,等于 val[root---->A] / val[root---->B]val[root---->B] / val[root---->A]

存储方式不需要直接建图,而是用并查集实现,并且可以实现路径压缩,将每个集合所有节点的val压缩成与根节点的val.

拿代码中的并查集方式来举例子.
首先将变量名映射为整型节点,默认值较小的节点为父节点.
假设D的序号相对C更小,作为C的父节点
给出关系 A / B = X , C / D = Y
那么val[B] = A / B =X , val[C] = D / C = 1 / Y

当要求值 B / C 时, 实际上是 (B / Z) * (Z / C)
假设我们已经获取了路径压缩的值 val[B] = Z / B , val[C] = Z / C

最终的结果即 B / C = val[C] / val[B]
leetcode 399. Evaluate Division(除法求值) [带权并查集]_第1张图片
在输入关系时进行路径压缩,并计算压缩后的权值.
最后遍历查询,直接判断是否有解,有解即用val中压缩好的权值相除即可

class Solution {
public:
    vector<int>fa;
    vector<double>val;
    int finds(int z)//并查集查询并路径压缩
    {
        if(fa[z]==z)
        {
            return z;
        }
        else
        {
            int tmp=fa[z];
            fa[z]=finds(fa[z]);
            val[z]*=val[tmp];
            return fa[z];
        }
    }
    vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
        map<string,int>m;
        m.clear();
        fa.clear();
        val.clear();
        int num=0;
        vector<double>ans;
        val.push_back(0);
        fa.push_back(0);
        for(int i=0;i<equations.size();i++)
        {
            string a=equations[i][0];
            string b=equations[i][1];
            if(m[a]==0)
            {
                m[a]=++num;
                fa.push_back(num);
                val.push_back(1);
            }
            if(m[b]==0)
            {
                m[b]=++num;
                fa.push_back(num);
                val.push_back(1);
            }
            if(m[a]<m[b])
            {
                val[m[b]]=values[i];
                fa[m[b]]=m[a];
                fa[m[b]]=finds(m[b]);//路径压缩
            }
            else 
            {
                val[m[a]]=1.0/values[i];//反边
                fa[m[a]]=m[b];
                fa[m[a]]=finds(m[a]);//路径压缩
            }
        }
        
        for(int i=0;i<queries.size();i++)
        {
            string a=queries[i][0];
            string b=queries[i][1];
            if(m[a]==0||m[b]==0)//未出现在关系式中底变量,直接无解
            {
                ans.push_back(-1.0);
                continue;
            }
            fa[m[a]]=finds(m[a]);
            fa[m[b]]=finds(m[b]);
            if(fa[m[a]]!=fa[m[b]])//不在同一个集合中的变量,没有任何关系,无解
            {
                ans.push_back(-1.0);
                continue;
            }
            double aa=val[m[a]];
            double bb=val[m[b]];
            ans.push_back(bb/aa);
        }
        return ans;
    }
};

你可能感兴趣的:(并查集,leetcode)