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)。