leetcode399. 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.
已知一些字母之間的關係式,問是否能夠計算出其它字母之間的倍數關係?
如已知a/b=2.0 b/c=3.0
問是否能夠計算出a/c, b/a, a/e, a/a, x/x
的值。如果無法計算得出,則返回-1。這裡x/x
的值因為在條件中無法獲知x是否等於零,因此也無法計算其真實結果,也需要返回-1。
思路和程式碼
假如我們將除數和被除數看做是圖的頂點,將除數和被除數之間的倍數關係試做二者之間邊的權重。即a/b=2.0
代表點a指向點b的邊的權重為2.0,而點b指向點a的邊的全中為1/2.0=0.5。
因此我們可以將輸入的表示式轉化為一個加權有向圖,而題目的問題則被轉化為求兩個點之間是否能夠找到一條邊,如果無法找到,則返回-1,否則返回路徑上每條邊的權重的乘積。
程式碼如下:
public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) { //圖的鏈式表示法 Map<String, List<String>> pairs = new HashMap<>(); //圖上每條邊的權重 Map<String, List<Double>> valuedPairs = new HashMap<>(); for(int i = 0 ; i < equations.size() ; i++) { //獲取第i個方程式 List<String> equation = equations.get(i); String multiplied = equation.get(0);//被除數 String multiplier = equation.get(1);//除數 //如果被除數從來沒有新增到圖中,則將其作為頂點在圖中初始化 if(!pairs.containsKey(multiplied)) { pairs.put(multiplied, new ArrayList<>()); valuedPairs.put(multiplied, new ArrayList<>()); } //如果除數從來沒有新增到圖中,則將其作為頂點在圖中初始化 if(!pairs.containsKey(multiplier)) { pairs.put(multiplier, new ArrayList<>()); valuedPairs.put(multiplier, new ArrayList<>()); } //新增邊和邊的權重 pairs.get(multiplied).add(multiplier); pairs.get(multiplier).add(multiplied); valuedPairs.get(multiplied).add(values[i]); valuedPairs.get(multiplier).add(1.0 / values[i]); } //結果集 double[] result = new double[queries.size()]; for(int i = 0 ; i<queries.size() ; i++) { //在圖中,以被除數作為頂點,深度優先遍歷圖,直到找到值為除數的頂點 result[i] = dfs(queries.get(i).get(0), queries.get(i).get(1), pairs, valuedPairs, new HashSet<>(), 1.0); result[i] = result[i]==0.0 ? -1.0 : result[i]; } return result; } public double dfs(String multiplied, String multiplier, Map<String, List<String>> pairs, Map<String, List<Double>> valuedPairs, Set<String> visited, double curResult) { //如果圖中不包含該被除數頂點,則無法獲知該表示式的值 if(!pairs.containsKey(multiplied)) return 0.0; //如果再次訪問過該被除數,說明找到了一條環路,則該深度優先遍歷結果失敗,直接拋棄 if(visited.contains(multiplied)) return 0.0; //如果被除數等於除數,則返回1.0 if(multiplied.equals(multiplier)) return curResult; visited.add(multiplied); //獲得當前被除數的所有鄰接頂點 List<String> multipliers = pairs.get(multiplied); //獲得所有鄰接邊的權重 List<Double> multiplierValues = valuedPairs.get(multiplied); double tmp = 0.0; for(int i = 0 ; i<multipliers.size() ; i++) { //以鄰接點為新的頂點,繼續深度優先遍歷 //此時呼叫方法中curResult的值代表的是該原鄰接點除以鄰接點的值 //如 a/b=2, b/c=3, 則a=2b,因此當我們以b作為鄰接點尋找c時,需要記錄原被除數是現被除數的兩倍 tmp = dfs(multipliers.get(i), multiplier, pairs, valuedPairs, visited, curResult * multiplierValues.get(i)); //找到非零路徑,結束深度優先遍歷 if(tmp != 0.0){ break; } } visited.remove(multiplied); return tmp; }