Graph+BFS/DFS
这道题最好想到的就是建立一个graph,两个节点的ratio就当做图的边保存下来。
query的时候,只要从一个节点开始,dfs或者bfs,把一路的ratio都乘起来即可。
用BFS来做:
class Solution { public: vector<double> calcEquation(vector<pair<string, string>> equations, vector<double>& values, vector<pair<string, string>> queries) { unordered_map<string, unordered_map<string, double>> graph; for (int i=0;i<equations.size();++i){ string a=equations[i].first, b=equations[i].second; graph[a][b] = values[i]; graph[b][a] = 1.0/values[i]; graph[a][a] = 1.0; graph[b][b] = 1.0; } vector<double> res; for (auto query:queries){ string a=query.first, b=query.second; if (!graph.count(a) || !graph.count(b)){ res.push_back(-1.0); continue; } queue<pair<string,double>> q; // <curNode, product so far> unordered_set<string> used({a}); bool find=false; q.push({a,1.0}); while (!q.empty()){ auto cur=q.front(); q.pop(); if (cur.first==b){ res.push_back(cur.second); find = true; break; } for (pair<string,double> x:graph[cur.first]){ if (!used.count(x.first)){ used.insert(x.first); x.second *= cur.second; q.push(x); } } } if (!find) res.push_back(-1.0); } return res; } };
Union Find
用图来做,既需要大量空间,而且dfs和bfs的效率都不是很高。
这道题也可以用Union Find来做,有关联的字符串就union在一起。每次union的时候,以root为基准,更新节点的值。
本题的关键是,当两个节点都已经存在时,这两个节点可能在不同的set里。为了保证 node1->val / node2->val = values[i],我们是要对一边的集合进行改动的。
假定把 root1 接到 root2 上,root2作为新的root,我们需要对 node1 所在的set里所有元素进行缩放,这个比例可以很简单计算出来:
double ratio = value * node2->val / node1->val;
相比graph,这种方法query很方便。如果不在一个set中,直接返回-1。在的话,直接两个节点的val相除即可,因为val都是相对root的。
class Solution { public: vector<double> calcEquation(vector<pair<string, string>> equations, vector<double>& values, vector<pair<string, string>> queries) { vector<double> res; for (int i=0;i<equations.size();++i){ string s1=equations[i].first, s2=equations[i].second; if (!m.count(s1) && !m.count(s2)){ m[s1] = new Node(values[i]); m[s2] = new Node(1.0); m[s1]->parent = m[s2]; }else if (!m.count(s1)){ // s2 exists m[s1] = new Node(m[s2]->val*values[i]); m[s1]->parent = m[s2]; }else if (!m.count(s2)){ // s1 exists m[s2] = new Node(m[s1]->val/values[i]); m[s2]->parent = m[s1]; }else{ unionSet(s1,s2,values[i]); } } for (auto query:queries){ string s1=query.first, s2=query.second; if (!m.count(s1) || !m.count(s2) || findSet(m[s1])!=findSet(m[s2])) res.push_back(-1.0); else{ res.push_back( m[s1]->val / m[s2]->val ); } } return res; } private: struct Node{ // for convenience, rank is not included double val; Node *parent; Node(double v):val(v),parent(this){} }; unordered_map<string, Node *> m; //node->val to Node * void unionSet(string s1, string s2, double value){ Node *node1=m[s1], *node2=m[s2]; Node *root1=findSet(node1), *root2=findSet(node2); double ratio = value * node2->val / node1->val; for (auto it=m.begin();it!=m.end();++it) if (findSet(it->second)==root1) it->second->val *= ratio; root1->parent = root2; } Node *findSet(Node *node){ if (node->parent==node) return node; node->parent = findSet(node->parent); return node->parent; } };
References: