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"] ]. 

花花:https://www.youtube.com/watch?v=UwpvInpgFmo

在这里插入图片描述

方法1: dfs

建立所有已知关系的双向图,以乘积为weight,然后用寻找queries当中路径的weight乘积。如果存在路径返回weight,如果不存在返回-1。起点和终点中有任意值不在图中返回-1。在每一个query中要沿途传递一个visitied,来记录已经访问过的路径,避免cycle。

需要的数据结构:

  1. unordered_map<string, unordered_map<string, double>>>>: {start : {end: weight}} 建立图, 这里选择unordered_map 主要由于没有什么有序性,对比207,210,course schedule,用到vector<vector<int>>, 310. minimum height tree,用到vector<unordered_set>
  2. unordered_set<string>: 对每一次query,记录经过的节点
  3. vector<double>:储存结果

易错点:

  1. 终止条件:当a == b,返回1.0
  2. 建立双向图,weight和1/weight
  3. 一定要预先判断start和end的存在:不只是为了efficiency,而是[“x”, “x”] 会返回1.0 而不是-1.0
  4. 如果不存在对应路径,最后会返回-1,如果存在返回graph[a][c] * dfs(c, b, graph, visited)
  5. 递归时要记录visited.insert(a): 作为双向图,也就是一定有环的图,必须记录visited

Complexity

时间: O(e + q * e), 如果每个query都需要所有节点,O(q * e), O(e) 是建立图的时间
空间:O(e)

grandyang: http://www.cnblogs.com/grandyang/p/5880133.html

class Solution {
public:
    vector<double> calcEquation(vector<vector<string>> equations, vector<double>& values, vector<vector<string>> queries) {
        vector<double> result;
        unordered_map<string, unordered_map<string, double>> hash;
        // construct graph
        for (int i = 0; i < equations.size(); i++) {
            hash[equations[i][0]][equations[i][1]] = values[i];
            hash[equations[i][1]][equations[i][0]] = 1 / values[i];
        }
        // query
        for (auto q: queries) {
            if (!hash.count(q[0]) || !hash.count(q[1])) result.push_back(-1.0);
            else {
                unordered_set<string> visited;
                result.push_back(calcHelper(hash, q[0], q[1],visited));
            }
        }
        return result;
    }
    
    double calcHelper(unordered_map<string, unordered_map<string, double>> & hash, string  start, string  end, unordered_set<string> & visited) {
        if (start == end) return 1.0;
        for (auto item: hash[start]){
            string intermediate = item.first;
            double segment = item.second;
            
            if (visited.count(intermediate)) continue;
            visited.insert(intermediate);
            double tmp = calcHelper(hash, intermediate, end, visited);
            if (tmp != -1) return tmp * segment;
        }
        return -1.0;
    }
};

花花:https://www.youtube.com/watch?v=UwpvInpgFmo

class Solution {
public:
    vector<double> calcEquation(vector<pair<string, string>> equations, vector<double>& values, vector<pair<string, string>> queries) {
        vector<double> result;
        // 给所有equations建立双向图
        unordered_map<string, unordered_map<string, double>> graph;
        for (int i = 0; i < equations.size(); i++){
            string A = equations[i].first;
            string B = equations[i].second;
            double weight= values[i];
            graph[A][B] = weight;
            graph[B][A] = 1 / weight;
        }
        
        for (auto q: queries){
            string  a = q.first;
            string  b = q.second;
            if (!graph.count(a) || !graph.count(b)){
                result.push_back(-1);
                continue;
            }
            // 为每次dfs建立一个visited集合,来避免cycle
            unordered_set<string> visited;
            double res = calcHelper(a, b, graph, visited);
            result.push_back(res);
        }
        return result;
    }
private:
    
    double calcHelper(string& a, string& b, unordered_map<string, unordered_map<string, double>> & graph, unordered_set<string> & visited){
        // 终止条件是
        if (a == b) return 1.0;
        visited.insert(a);
        for (auto pair: graph[a]){
            string c = pair.first;
            if (!visited.count(c)){
                double d = calcHelper(c, b, graph, visited);
                if (d != -1) {
                    return d * graph[a][c];
                }
            }
        }
        return -1.0;
    }  
};

方法2:Union Find

Complexity

时间:O(e + q)
空间: O(e)

Union find可以在O(1)时间查找parent和path

易错点:

  1. child / parent = weight
  2. 更新祖先,同时要更新祖先的weight
// Author: Huahua
// Runtime: 3 ms
class Solution {
public:
  vector<double> calcEquation(const vector<pair<string, string>>& equations, vector<double>& values, const vector<pair<string, string>>& queries) {
  // parents["A"] = {"B", 2.0} -> A = 2.0 * B
  // parents["B"] = {"C", 3.0} -> B = 3.0 * C
  unordered_map<string, pair<string, double>> parents;
 
  for (int i = 0; i < equations.size(); ++i) {
    const string& A = equations[i].first;
    const string& B = equations[i].second;
    const double k = values[i];
    // Neighter is in the forrest
    if (!parents.count(A) && !parents.count(B)) {
      parents[A] = {B, k};
      parents[B] = {B, 1.0};
    } else if (!parents.count(A)) {
      parents[A] = {B, k};
    } else if (!parents.count(B)) {
      parents[B] = {A, 1.0 / k};
    } else {
      auto& rA = find(A, parents);
      auto& rB = find(B, parents);      
      parents[rA.first] = {rB.first, k / rA.second * rB.second};
    }    
  }
 
  vector<double> ans;
  for (const auto& pair : queries) {
    const string& X = pair.first;
    const string& Y = pair.second;
    if (!parents.count(X) || !parents.count(Y)) {
      ans.push_back(-1.0);
      continue;
    }
    auto& rX = find(X, parents); // {rX, X / rX}
    auto& rY = find(Y, parents); // {rY, Y / rY}
    if (rX.first != rY.first)
      ans.push_back(-1.0);
    else // X / Y = (X / rX / (Y / rY))
      ans.push_back(rX.second / rY.second);
  }
  return ans;
}
private:
  pair<string, double>& find(const string& C, unordered_map<string, pair<string, double>>& parents) {
    if (C != parents[C].first) {
      const auto& p = find(parents[C].first, parents);
      parents[C].first = p.first;
      parents[C].second *= p.second;
    }
    return parents[C];
  }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值