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