• [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 题目汇总

  • 相关阅读:
    spring
    C++容器常用方法简单总结
    【转】shell中各种括号的作用详解()、(())、[]、[[]]、{}
    c++创建对象时一些小细节
    ros建模与仿真(urdf介绍)
    常用vi命令
    Linux零零碎碎的小知识
    Linux目录都是些什么
    关于c/c++指针,指针的指针
    关于c/c++中的二维数组与指针
  • 原文地址:https://www.cnblogs.com/lightwindy/p/9750133.html
Copyright © 2020-2023  润新知