LeetCode: 399. Evaluate Division
题目比较好理解哈,定义两个string的商,然后发起一系列查询,需要输出可以推出的商,不能得出的商为-1.0。这题目有几点要注意的:
- x/x=-1.0虽然我们知道就算不知道x的值,但是题目其实已经确保了x不为0,那么实际上答案应该是1.0,但题目的例程把它定义为-1.0。当然是题意最大啦。
- 不考虑无效输入,同时会保证分母不是0
既然这个题目的分类是Graphs,自然可以用有向图来解决问题了。每个string是点的值(唯一标识),有向边的值表示a/b,其中a是出点,b是入点。
很自然的一个思路是:
- 建图:根据每个输入得到a/b=k和b/a=1/k两条有向边,时间复杂度O(n)
- 遍历:遍历搜索是否有解,时间复杂度O(n)(最坏情况历遍所有的点才能得到解)
代码
遍历就少不了DFS还是BFS了,DFS形式上简单,但是要注意停止条件和难以控制递归层数。BFS则写起来复杂些,而且需要额外的空间来存储压栈但尚未处理的点和中间的搜索值(来加快在大查询量的情况下的搜索速度,避免重复计算一些对的商,这个思想可以参考斐波那契数列的空间换时间思想)。
下面是用DFS写的。为了方便也为了快速,用unorder_map来做graphs的结构,事实上可以算是一种邻接表的思想,但是搜索插入删除的复杂度都是O(1)。要注意的是:visited的设置,避免重复搜索。
unordered_map<string,vector<pair<string,double>>> graphs;
unordered_set<string> visited;
double thisOne;
void dfs (string pstart, string pend, double val)
{
if (pstart == pend)
{
thisOne = val;
return;
}
vector<pair<string, double>> temps = graphs[pstart];
for (auto tmp : temps)
{
if (visited.find(tmp.first) == visited.end())
{
visited.insert(tmp.first);
dfs(tmp.first, pend, val * tmp.second);
}
if (thisOne)
break;
}
}
vector<double> calcEquation(vector<pair<string, string>> equations, vector<double>& values, vector<pair<string, string>> queries)
{
vector<double> ans(queries.size());
//bulid the map
for(int i=0; i<equations.size(); i++)
{
graphs[equations[i].first].push_back(make_pair(equations[i].second,values[i]));
graphs[equations[i].second].push_back(make_pair(equations[i].first,1.0 / values[i]));
}
//query
for(int i = 0; i<queries.size(); i++)
{
visited.clear();
thisOne =0;
string pstart = queries[i].first;
string pend = queries[i].second;
if(graphs.find(pstart) ==graphs.end() || graphs.find(pend) == graphs.end())
{
ans[i]=-1.0;
}
else
{
visited.insert(pstart);
dfs(pstart,pend, 1.0);
if (thisOne)
ans[i] = thisOne;
else
ans[i] = -1;
}
}
}
BFS感觉会快些,就是每次搜索完把新的结果放进graphs里面,这样后面搜索就快了。不过貌似testcase都不大,所以这道题很多人用dfs0ms的。
拓展
其实有种更加快的,是用并查集。理解+基本实现参考,简单说就是用连通性把所有连通子集都作为一个并查集。可以把复杂度从O(n)降到O(logn)。