[leetcode每日一題2021/1/13]684. 冗餘連線
冗餘連線
題目來源於leetcode,解法和思路僅代表個人觀點。傳送門。
難度:中等
題目
在本問題中, 樹指的是一個連通且無環的無向圖。
輸入一個圖,該圖由一個有著N個節點 (節點值不重複1, 2, …, N) 的樹及一條附加的邊構成。附加的邊的兩個頂點包含在1到N中間,這條附加的邊不屬於樹中已存在的邊。
結果圖是一個以邊組成的二維陣列。每一個邊的元素是一對[u, v] ,滿足 u < v,表示連線頂點u 和v的無向圖的邊。
返回一條可以刪去的邊,使得結果圖是一個有著N個節點的樹。如果有多個答案,則返回二維陣列中最後出現的邊。答案邊 [u, v] 應滿足相同的格式 u < v。
示例 1:
輸入: [[1,2], [1,3], [2,3]]
輸出: [2,3]
解釋: 給定的無向圖為:
1
/ \
2 - 3
示例 2:
輸入: [[1,2], [2,3], [3,4], [1,4], [1,5]]
輸出: [1,4]
解釋: 給定的無向圖為:
5 - 1 - 2
| |
4 - 3
注意:
輸入的二維陣列大小在 3 到 1000。 二維陣列中的整數在1到N之間,其中N是輸入陣列的大小。
思路
思路與此題類似
並查集入門-990. 等式方程的可滿足性
並查集
正常思路:選擇一條邊去掉,使得該圖的連通分量不變。但是如果這樣程式設計的話,每次用dfs遍歷該圖,會試得時間複雜度
遍歷edges陣列,構建一個圖,使得其連通分量為1
檢查兩個頂點是否在同一個集合中
否:合併兩個頂點所在的集合
是:返回該邊作為答案(由於圖有N個頂點,第一條出現的邊,就是二維陣列中最後出現的答案)
該題,使用並查集的特點:無向圖的連通性
程式碼
class Solution {
public:
//構建並查集
class UnionFind{
public:
vector<int> parent;
UnionFind(int n){
parent.resize(n);
for(int i=0;i<n;i++){
parent[i] = i;
}
}
//合併 兩個頂點所在的集合
void join(int x,int y){
int rootX = find(x);
int rootY = find(y);
// x -> y
parent[rootX] = rootY;
}
//查詢一個頂點的 父節點
int find(int x){
while(parent[x] != x){
//路徑壓縮
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}
//判斷兩個頂點是否在同一個集合中
bool isConnected(int x,int y){
return find(x) == find(y);
}
};
vector<int> findRedundantConnection(vector<vector<int>>& edges) {
vector<int> ans(2);
//依次取陣列中的邊,利用並查集歸併
int N = edges.size();
UnionFind unionFind(N+1);
for(int i=0;i<N;i++){
int x = edges[i][0];
int y = edges[i][1];
if(!unionFind.isConnected(x,y)){
//如果不在同一個集合中,就將兩個集合合併
unionFind.join(x,y);
}else{
//如果在同一個集合中,就得出答案
ans[0] = x;
ans[1] = y;
}
}
return ans;
}
};
演算法複雜度
引自-官方題解
時間複雜度: O(NlogN),其中 N 是圖中的節點個數。需要遍歷圖中的 N 條邊,對於每條邊,需要對兩個節點查詢祖先,如果兩個節點的祖先不同則需要進行合併,需要進行 2 次查詢和最多 1 次合併。一共需要進行 2N 次查詢和最多 N 次合併,因此總時間複雜度是 O(2NlogN)=O(NlogN)。這裡的並查集使用了路徑壓縮,但是沒有使用按秩合併,最壞情況下的時間複雜度是O(NlogN),平均情況下的時間複雜度依然是O(Nα(N)),其中 α 為阿克曼函式的反函式,α(N) 可以認為是一個很小的常數。
空間複雜度: O(N),其中 N 是圖中的節點個數。使用陣列parent 記錄每個節點的祖先。