leetcode-399-除法求值-java

题目及测试

package pid399;
/*  399. 除法求值

给出方程式 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的情况,且不存在任何矛盾的结果。

*/

import java.util.ArrayList;
import java.util.List;


public class main {
	
	public static void main(String[] args) {
		List list = null;		
		List> ito1 = new ArrayList>();
		list = new ArrayList<>();
		list.add("a");
		list.add("b");
		ito1.add(list);
		list = new ArrayList<>();
		list.add("b");
		list.add("c");
		ito1.add(list);
		
		double[] ito2 = new double[]{2,3};
		
		List> ito3 = new ArrayList>();
		list = new ArrayList<>();
		list.add("a");
		list.add("c");
		ito3.add(list);
		list = new ArrayList<>();
		list.add("b");
		list.add("a");
		ito3.add(list);
		list = new ArrayList<>();
		list.add("a");
		list.add("e");
		ito3.add(list);
		list = new ArrayList<>();
		list.add("a");
		list.add("a");
		ito3.add(list);
		list = new ArrayList<>();
		list.add("x");
		list.add("x");
		ito3.add(list);
		
	}
		 
	private static void test(List> ito1, double[] ito2, List> ito3) {
		Solution solution = new Solution();
		double[] rtn;
		long begin = System.currentTimeMillis();
		System.out.println("ito1=");
		System.out.println();
		for(int i=0;i

没想出来

解法1(别人的)

首先如果有a/b = 3,我们令parent[a] = b,即让b成为a的父亲,同时让val[a] = 3。
所以此处的定义非常重要:即val[]数组存放的是节点与他直接父节点的关系
因此我们在遍历equations时,根据from/to = val 的关系,要初始化一些指向和val数组的值。
但是在from已经存在(即parent里存在这个key),说明from已经在某个集合中(作为根或非根),我们为了方便起见(不需要合并集合),让不在集合的to指向from,val也取倒数。

最关键的一步在于,如果from和to在两个不同的集合内怎么办?
这时候注意看我们的find函数,find函数除了返回根以外,还进行了路径压缩以及val值更新,我们利用回溯对val值进行了更新,使得val值实际上变成了节点与根的关系,而且每个非根节点还会直接指向根节点。(路径压缩也保证了多次find操作不会使得val值被多次错误更新,因为根节点的val值是1)
也就是说如果一个集合有a/b = 3, b/c = 3,其中c作为根节点。那么在find返回之后,我们不仅拿到了c,还使得集合的形状变为了:

    a -> c,val[a] = 9
    b -> c, val[b] = 3
    c -> c, val[c] = 1

那么回到我们from和to在两个不同集合的问题,我们发现在判断from和to是否位于一个集合中的时候,我们分别对from和to各调用了一次find操作,这说明什么?
说明此时from和to已经直接指向他们各自的根节点,并且val[from]和val[to]直接存放他们和各自根节点的比值
现在我们要对两个集合进行合并,假设from的根为root1, to的根为root2,我们让parent[root1] = root2
注意,这时候val[root1]不能再为1了,那么我们要更新为多少呢?
其实我们发现val[root1] 就是 root1 / root2的值
而且我们已有from/root1, to/root2, from/to的值
简单的数学:root1/root2 = to/root2 * from/to ÷ from/root1也就是val[root1] = val[to] * from/to ÷ val[from]

好了此时我们的val[root1]已经是正确的值了,它确实反映了root1和root2的正确关系。至于原本在root2为根的集合的节点,它们是不用发生任何变化的,这点很显然。
那么原本在root1集合中的点呢?
我们惊奇的发现,由于val[root1]已经不再是1了,因此在下一次find()操作中,其他节点会同样经过回溯更新val值,使得val值最终成为该节点与新根节点root2的比值。

非常完美!而且由于我们在query阶段,绝对会对from和to各自调用一次find操作,确保了这之后val[from]和val[to]都是必定正确的,也就是他们都真实反映了他们和各自根节点的比值。

因此如果from和to位于同一集合(假设根为root),也就是我们根据val能得到绝对正确的 from / root 和 to / root,那么 from / to等于多少呢?
这下问题就变成了小学数学了,拿下!

class Solution {
    static Map parents;
    static Map val;


    public String find(String x) {

        if (!x.equals(parents.get(x))) {
            String tmpParent = parents.get(x);
            String root = find(tmpParent);
            double oldVal = val.get(x);
            val.put(x, oldVal * val.get(tmpParent));
            parents.put(x, root);
        }
        return parents.get(x);
    }

    public double[] calcEquation(List> equations, double[] values, List> queries) {
        parents = new HashMap<>();
        val = new HashMap<>();
        int i = 0;
        for (List equation : equations) {
            String from = equation.get(0);
            String to = equation.get(1);
            double cur = values[i];
            if (!parents.containsKey(from) && !parents.containsKey(to)) {
                parents.put(from, to);
                val.put(from, cur);
                parents.put(to, to);
                val.put(to, 1.0);
            } else if (!parents.containsKey(from)) {
                parents.put(from, to);
                val.put(from, cur);
            } else if (!parents.containsKey(to)) {
                parents.put(to, from);
                val.put(to, 1 / cur);
            } else {
                String pa = find(from);
                String pb = find(to);
                if (!pa.equals(pb)) {
                    parents.put(pa, pb);
                    val.put(pa, cur * val.get(to) / val.get(from));
                }
            }
            i++;
        }
        i = 0;
        double[] res = new double[queries.size()];
        for (List query : queries) {
            String from = query.get(0);
            String to = query.get(1);
            if (!parents.containsKey(from) || !parents.containsKey(to)) {
                res[i++] = -1;
                continue;
            }
            String pa = find(from);
            String pb = find(to);
            if (!pa.equals(pb)) res[i] = -1;
            else {
                res[i] = val.get(from) / val.get(to);
            }
            i++;
        }
        return res;
    }
}

 

 

你可能感兴趣的:(leetcode-中等,leetcode,数据结构-图)