leetcode 第399題 除法求值
問題分析
問題如下(傳送門)
題目給了一組方程式,每個方程式有兩個變量,分別爲被除數和除數,最後的商在對應位置的方程式結果中。最後求的就是在問題方程式中解出每個方程。這個題目可以用並查集的方法來解決,我們可以將具有聯繫的所有變量歸類到同一個集合中,在一個集合中,任意兩個變量直接的關係都可以得到,所有隻要問題方程式中的兩個變量在同一個集合就能很方便的得到兩者的商。
下面的問題就是用什麼數據結構來構建這樣的集合。開始,我用的是一個長度爲26的數組,這樣每個字母對應唯一的位置,但是後來提交的時候失敗了,因爲題目所給的變量並不是全部有單一的字母構成的,其實變量是字符串。所有,後來我就換成用字典來求解了。在字典中,鍵爲當前字符串變量,值爲一個列表,其中第一個值爲當前鍵的父親(形象一點),初始化時就爲自己本身,第二個值時父親與當前鍵的比值,初始化爲1.0,如下所示。
接下來就是構建集合了,假設給定的方程式爲[ [“a”, “b”], [“b”, “c”]],我們可以得到下面的結構:
其中b的父親爲a,b的值爲(“a”, a/b),c開始的父親爲b,所以c的值爲(“b”, b/c),但後來將c的父親指向a,如果將c的父親指向a,那麼c值的第二項就要改爲a/c,可以通過a/b * b/c得到,這樣做對以後的計算很有幫助,因爲可以很快速的找到其父親。
上面的情況中,如果在這條父子鏈上,多出分支的話,可以直接加入,即假設方程式爲[ [“a”, “b”], [“b”, “c”], [“b”, “e”]],得到字典如下:
同樣,改變e的父親到a。
上面是所有的變量構成了一個集合,下面是有兩個集合的情況。假設方程式爲[ [“a”, “b”], [“b”, “c”],[“b”, “e”], [“f”, “g”]],那麼結果爲:
接下來就是比價重要的點了,假設在給定方程式中先建立了兩個不同的集合,但是有一個方程式將兩個集合聯繫到一起那該怎麼辦,比如方程式爲:[ [“a”, “b”], [“b”, “c”], [“b”, “e”], [“f”, “g”], [“c”, “g”]]。通過c和g將兩個集合合併成一個,這裏我的做法是,找到兩個結合的父親,然後讓其中一個父親降級,成爲另一個父親的兒子,這裏,我讓f成爲了a的兒子,那麼得到示意圖如下:
我們知道如下項:a/c, c/g, f/g所以一定能得到a/f(數學運算)。至此,所有的變量都聯通了。
下面就是對問題方程式求值的過程。由於所有在同一個集合的變量都有同一個祖先,我們就可以利用這個祖先做一箇中間項。比如b/g。可以用下面的手段得到:
所以只要是同一個集合,一定得到確切的值,不在同一個集合的或在方程式中沒有出現的變量,直接返回-1.0。
源碼
Python
class Solution:
def calcEquation(self, equations, values, queries):
conn = {}
ret = []
def findFather(i):
multi = 1
while conn[i][0] != i:
multi *= conn[i][1]
i = conn[i][0]
return i, multi
for i, equ in enumerate(equations):
a, b = equ
if a in conn:
father_1, num_1 = findFather(a)
num_2 = 1
father_2 = b
if b in conn:
father_2, num_2 = findFather(b)
conn[father_2] = father_1, num_1 * values[i] / num_2
else:
conn[a] = [a, 1]
num_2 = 1
father_2 = b
if b in conn:
father_2, num_2 = findFather(b)
conn[father_2] = a, values[i] / num_2
for query in queries:
a, b = query
if a in conn and b in conn:
father_1, num_1 = findFather(a)
father_2, num_2 = findFather(b)
if father_1 == father_2:
ret.append(num_2 / num_1)
continue
ret.append(-1.0)
return ret
C++
class Solution {
map<string, pair<string, double>> conn;
string father_2, father_1, a, b;
double num_1, num_2;
pair<string, double> ans_1, ans_2;
pair<string, double> findFather(string i){
double multi = 1.0;
while (conn[i].first != i){
multi *= conn[i].second;
i = conn[i].first;
}
return make_pair(i, multi);
}
public:
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
vector<double> ret;
for (int i = 0; i < equations.size(); ++i){
a = equations[i][0];
b = equations[i][1];
if (conn.find(a) != conn.end()){
ans_1 = findFather(a);
father_1 = ans_1.first;
num_1 = ans_1.second;
father_2 = b;
num_2 = 1.0;
if (conn.find(b) != conn.end()){
pair<string, double> ans_2 = findFather(b);
father_2 = ans_2.first;
num_2 = ans_2.second;
}
conn[father_2] = make_pair(father_1, num_1 * values[i] / num_2);
}
else{
conn[a] = make_pair(a, 1.0);
father_2 = b;
num_2 = 1.0;
if (conn.find(b) != conn.end()){
pair<string, double> ans_2 = findFather(b);
father_2 = ans_2.first;
num_2 = ans_2.second;
}
conn[father_2] = make_pair(a, values[i] / num_2);
}
}
for (auto query: queries){
a = query[0];
b = query[1];
if (conn.find(a) != conn.end() && conn.find(b) != conn.end()){
ans_1 = findFather(a);
ans_2 = findFather(b);
if (ans_1 .first == ans_2.first){
ret.push_back(ans_2.second / ans_1.second);
continue;
}
}
ret.push_back(-1.0);
}
return ret;
}
};