[LeetCode] 399. Evaluate Division 求除法表达式的值

Equations are given in the format A / B = k, where A and B are variables represented as strings, and k is a real number (floating point number). Given some queries, return the answers. If the answer does not exist, return -1.0.

Example:
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 ].

The input is: vector<pair<string, string>> equations, vector<double>& values, vector<pair<string, string>> queries, where equations.size() == values.size(), and the values are positive. This represents the equations. Return vector<double>.

According to the example above:

equations = [ ["a", "b"], ["b", "c"] ],
values = [2.0, 3.0],
queries = [ ["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"] ]. 

The input is always valid. You may assume that evaluating the queries will result in no division by zero and there is no contradiction.

给一些有结果的除法等式,又给了一些除法式子要查询结果,根据已知除法等式求结果,没有答案的返回-1。

解法1:Graph,Image a/b = k as a link between node a and b, the weight from a to b is k, the reverse link is 1/k. Query is to find a path between two nodes.

解法2:Union Find

解法3:Hash + DFS

Java: Graph

public double[] calcEquation(String[][] equations, double[] values, String[][] queries) {
        HashMap<String, ArrayList<String>> pairs = new HashMap<String, ArrayList<String>>();
        HashMap<String, ArrayList<Double>> valuesPair = new HashMap<String, ArrayList<Double>>();
        for (int i = 0; i < equations.length; i++) {
            String[] equation = equations[i];
            if (!pairs.containsKey(equation[0])) {
                pairs.put(equation[0], new ArrayList<String>());
                valuesPair.put(equation[0], new ArrayList<Double>());
            }
            if (!pairs.containsKey(equation[1])) {
                pairs.put(equation[1], new ArrayList<String>());
                valuesPair.put(equation[1], new ArrayList<Double>());
            }
            pairs.get(equation[0]).add(equation[1]);
            pairs.get(equation[1]).add(equation[0]);
            valuesPair.get(equation[0]).add(values[i]);
            valuesPair.get(equation[1]).add(1/values[i]);
        }
        
        double[] result = new double[queries.length];
        for (int i = 0; i < queries.length; i++) {
            String[] query = queries[i];
            result[i] = dfs(query[0], query[1], pairs, valuesPair, new HashSet<String>(), 1.0);
            if (result[i] == 0.0) result[i] = -1.0;
        }
        return result;
    }
    
    private double dfs(String start, String end, HashMap<String, ArrayList<String>> pairs, HashMap<String, ArrayList<Double>> values, HashSet<String> set, double value) {
        if (set.contains(start)) return 0.0;
        if (!pairs.containsKey(start)) return 0.0;
        if (start.equals(end)) return value;
        set.add(start);
        
        ArrayList<String> strList = pairs.get(start);
        ArrayList<Double> valueList = values.get(start);
        double tmp = 0.0;
        for (int i = 0; i < strList.size(); i++) {
            tmp = dfs(strList.get(i), end, pairs, values, set, value*valueList.get(i));
            if (tmp != 0.0) {
                break;
            }
        }
        set.remove(start);
        return tmp;
    }  

Java: Union Find

class Solution {
    class Node{
        String label;
        double val;
        public Node(String label, double val){this.label=label;this.val=val;}
    }
    public double[] calcEquation(String[][] equations, double[] values, String[][] queries) {
        Map<String, Node> map= new HashMap<>();
        for (int i=0; i<equations.length; i++){
            String n= equations[i][0], d= equations[i][1];
            Node N= find(map, n), D= find(map, d);
            if (!N.label.equals(D.label)) map.put(N.label, new Node(D.label, values[i]*D.val/N.val));
        }
        double[] res= new double[queries.length];
        for (int i=0; i<res.length; i++){
            res[i]=-1.0;
            String n=queries[i][0], d=queries[i][1];
            if (!map.containsKey(n) || !map.containsKey(d)) continue;
            Node N= find(map, n), D= find(map, d);
            if (!N.label.equals(D.label)) continue;
            res[i]=N.val/D.val;
        }
        return res;
    }
    public Node find(Map<String, Node> map, String cur){
        if (!map.containsKey(cur)) map.put(cur, new Node(cur, 1.0));
        Node curNode= map.get(cur);
        double val= 1.0;
        while (!map.get(cur).label.equals(cur)){
            val*=map.get(cur).val;
            cur=map.get(cur).label;
        }
        curNode.label=cur;
        curNode.val=val;
        return curNode;
    }
}  

Java:

public double[] calcEquation(String[][] equations, double[] values, String[][] query) {
        double[] result = new double[query.length];
        // filter unexpected words
        // 过滤掉没有遇见过的字符
        Set<String> words = new HashSet<>();
        for (String[] strs : equations) {
            words.add(strs[0]);
            words.add(strs[1]);
        }
        for (int i = 0; i < query.length; ++i) {
            String[] keys = query[i];
            if (!words.contains(keys[0]) || !words.contains(keys[1])) result[i] = -1.0d;
            else {
                Stack<Integer> stack = new Stack<>();
                result[i] = helper(equations, values, keys, stack);
            }
        }
        return result;
    }

    public double helper(String[][] equations, double[] values, String[] keys, Stack<Integer> stack) {
        // 直接查找,key的顺序有正反
        // look up equations directly
        for (int i = 0; i < equations.length; ++i) {
            if (equations[i][0].equals(keys[0]) && equations[i][1].equals(keys[1])) return values[i];
            if (equations[i][0].equals(keys[1]) && equations[i][1].equals(keys[0])) return 1 / values[i];
        }
        // lookup equations by other equations
        // 间接查找,key的顺序也有正反
        for (int i = 0; i < equations.length; ++i) {
            if (!stack.contains(i) && keys[0].equals(equations[i][0])) {
                stack.push(i);
                double temp = values[i] * helper(equations, values, new String[]{equations[i][1], keys[1]}, stack);
                if (temp > 0) return temp;
                else stack.pop();
            }
            if (!stack.contains(i) && keys[0].equals(equations[i][1])) {
                stack.push(i);
                double temp = helper(equations, values, new String[]{equations[i][0], keys[1]}, stack) / values[i];
                if (temp > 0) return temp;
                else stack.pop();
            }
        }
        // 查不到,返回-1
        return -1.0d;
    }  

Java: Union find

public double[] calcEquation(String[][] equations, double[] values, String[][] query) {

        // map string to integer
        Map<String, Integer> mIdTable = new HashMap<>();
        int len = 0;
        for (String[] words : equations)
            for (String word : words)
                if (!mIdTable.containsKey(word)) mIdTable.put(word, len++);

        // init parent index and value
        Node[] nodes = new Node[len];
        for (int i = 0; i < len; ++i) nodes[i] = new Node(i);

        // union, you can take an example as follows
        // (a/b=3)->(c/d=6)->(b/d=12)
        for (int i = 0; i < equations.length; ++i) {
            String[] keys = equations[i];
            int k1 = mIdTable.get(keys[0]);
            int k2 = mIdTable.get(keys[1]);
            int groupHead1 = find(nodes, k1);
            int groupHead2 = find(nodes, k2);
            nodes[groupHead2].parent = groupHead1;
            nodes[groupHead2].value = nodes[k1].value * values[i] / nodes[k2].value;
        }

        // query now
        double[] result = new double[query.length];
        for (int i = 0; i < query.length; ++i) {
            Integer k1 = mIdTable.get(query[i][0]);
            Integer k2 = mIdTable.get(query[i][1]);
            if (k1 == null || k2 == null) result[i] = -1d;
            else {
                int groupHead1 = find(nodes, k1);
                int groupHead2 = find(nodes, k2);
                if (groupHead1 != groupHead2) result[i] = -1d;
                else result[i] = nodes[k2].value / nodes[k1].value;
            }
        }
        return result;
    }

    public int find(Node[] nodes, int k) {
        int p = k;
        while (nodes[p].parent != p) {
            p = nodes[p].parent;
            // compress
            nodes[k].value *= nodes[p].value;
        }
        // compress
        nodes[k].parent = p;
        return p;
    }

    private static class Node {
        int    parent;
        double value;

        public Node(int index) {
            this.parent = index;
            this.value = 1d;
        }
    }  

Python: Union Find

class Solution:
    def calcEquation(self, equations, values, queries):
        """
        :type equations: List[List[str]]
        :type values: List[float]
        :type queries: List[List[str]]
        :rtype: List[float]
        """
        res = []
        parent = {}    # i.e. [a, b] then parent[a] = b
        weight = {}    # i.e. a / b = 2.0, then weight[a] = 2.0
        ufind = UnionFind(parent, weight)
        for i, edge in enumerate(equations):
            x1, x2 = edge[0], edge[1]
            val = values[i]
            if x1 not in parent and x2 not in parent:
                parent[x1] = x2
                parent[x2] = x2
                weight[x1] = val
                weight[x2] = 1
            elif x1 not in parent:
                parent[x1] = x2
                weight[x1] = val
            elif x2 not in parent:    # weight[x1] already exists, if make x2 be x1's parent. then weight[x1] will be overwrite.
                parent[x2] = x1
                weight[x2] = 1 / val
            else:
                ufind.union(x1, x2, val)
                
        for x1, x2 in queries:
            if x1 not in parent or x2 not in parent or ufind.find(x1) != ufind.find(x2):
                res.append(-1.0)
            else:
                factor1 = weight[x1]
                factor2 = weight[x2]
                res.append(factor1 / factor2)
        return res
        
class UnionFind():
    def __init__(self, parent, weight):
        self.parent = parent
        self.weight = weight

    def find(self, vertex):
        if self.parent[vertex] == vertex:
            return vertex
        root = self.find(self.parent[vertex])
        self.weight[vertex] *= self.weight[self.parent[vertex]]
        self.parent[vertex] = root
        return root

    def union(self, vertex1, vertex2, val):
        root1 = self.find(vertex1)
        root2 = self.find(vertex2)
        self.weight[root1]= self.weight[vertex2] * val / self.weight[vertex1]
        self.parent[root1] = root2  

Python:  Union Find + Path Compression + Rank 

def calcEquation(self, equations, values, queries):
        """
        Time: O(E+Q) , Union is approx O(1) because it's using path compression during find.
        Space:  O(E)
        """
        self.parents = {}
        self.weights = {} # Presents it as the val point to another graph
        self.rank = {}
        for (w, v), val in zip(equations, values):
            if w not in self.parents:
                self.parents[w] = w
                self.weights[w] = 1.0
                self.rank[w] = 1
            if v not in self.parents:
                self.parents[v] = v
                self.weights[v] = 1.0
                self.rank[v] = 1
            self.union(w, v, val)
        res = []
        for query in queries:
            w, v = query
            if w not in self.parents or v not in self.parents:
                res.append(-1.0)
                continue

            p1, p2 = self.find(w), self.find(v)
            if p1 != p2:
                res.append(-1.0)
            else:
                res.append(self.weights[w] / self.weights[v])
        return res
    
    def find(self, node):
        if node != self.parents[node]:
            p = self.parents[node]
            self.parents[node] = self.find(p)
            self.weights[node] = self.weights[node] * self.weights[p]
        return self.parents[node]
    
    def union(self, u, v, val):
        p1 = self.find(u)
        p2 = self.find(v)
        if self.rank[p1] > self.rank[p2]:
            p1, p2 = p2, p1
            val = 1 / val
            v, u = u, v
        if p1 != p2:
            self.parents[p1] = p2
            self.rank[p2] += self.rank[p1]
            self.rank[p1] = 1
            self.weights[p1] = (self.weights[v] / self.weights[u] ) * val 

C++:  Union Find

class Solution {
public:
    vector<double> calcEquation(vector<pair<string, string>> equations, vector<double>& values, vector<pair<string, string>> queries) {
        unordered_map<string, Node*> map;
        vector<double> res;
        for (int i = 0; i < equations.size(); i ++) {
            string s1 = equations[i].first, s2 = equations[i].second;
            if (map.count(s1) == 0 && map.count(s2) == 0) {
                map[s1] = new Node();
                map[s2] = new Node();
                map[s1] -> value = values[i];
                map[s2] -> value = 1;
                map[s1] -> parent = map[s2];
            } else if (map.count(s1) == 0) {
                map[s1] = new Node();
                map[s1] -> value = map[s2] -> value * values[i];
                map[s1] -> parent = map[s2];
            } else if (map.count(s2) == 0) {
                map[s2] = new Node();
                map[s2] -> value = map[s1] -> value / values[i];
                map[s2] -> parent = map[s1];
            } else {
                unionNodes(map[s1], map[s2], values[i], map);
            }
        }

        for (auto query : queries) {
            if (map.count(query.first) == 0 || map.count(query.second) == 0 || findParent(map[query.first]) != findParent(map[query.second]))
                res.push_back(-1);
            else
                res.push_back(map[query.first] -> value / map[query.second] -> value);
        }
        return res;
    }
    
private:
    struct Node {
        Node* parent;
        double value = 0.0;
        Node()  {parent = this;}
    };
    
    void unionNodes(Node* node1, Node* node2, double num, unordered_map<string, Node*>& map) {
        Node* parent1 = findParent(node1), *parent2 = findParent(node2);
        double ratio = node2 -> value * num / node1 -> value;
        for (auto it = map.begin(); it != map.end(); it ++)
            if (findParent(it -> second) == parent1)
                it -> second -> value *= ratio;
        parent1 -> parent = parent2;
    }
    
    Node* findParent(Node* node) {
        if (node -> parent == node)
            return node;
        node -> parent = findParent(node -> parent);
        return node -> parent;
    }
};

C++: Hash + DFS  

class Solution {
public:
    vector<double> calcEquation(vector<pair<string, string>> equations, 
        vector<double>& values, vector<pair<string, string>> query) 
    {
        unordered_map<string,unordered_map<string, double>> m;
        vector<double> res;
        for (int i = 0; i < values.size(); ++i)
        {
            m[equations[i].first].insert(make_pair(equations[i].second,values[i]));
            if(values[i]!=0)
                m[equations[i].second].insert(make_pair(equations[i].first,1/values[i]));
        }

        for (auto i : query)
        {
            unordered_set<string> s;
            double tmp = check(i.first,i.second,m,s);
            if(tmp) res.push_back(tmp);
            else res.push_back(-1);
        }
        return res;
    }

    double check(string up, string down, 
            unordered_map<string,unordered_map<string, double>> &m,
            unordered_set<string> &s)
    {
        if(m[up].find(down) != m[up].end()) return m[up][down];
        for (auto i : m[up])
        {
            if(s.find(i.first) == s.end())
            {
                s.insert(i.first);
                double tmp = check(i.first,down,m,s);
                if(tmp) return i.second*tmp;
            }
        }
        return 0;
    }
};

  

 

 

 

All LeetCode Questions List 题目汇总

 

posted @ 2018-10-07 14:48  轻风舞动  阅读(1143)  评论(0编辑  收藏  举报