685. Redundant Connection II
阿新 • • 發佈:2020-08-08
問題:
的第二版本,由原來的無向圖->有向圖
那麼給定一組edge [u,v],定義從頂點u到v的連線,構成有向圖。parent[v]=u,u->v
求最後一個多餘出來的[u,v],使得出現了迴環。(若沒有這個連線,則可形成tree)
Example 1: Input: [[1,2], [1,3], [2,3]] Output: [2,3] Explanation: The given directed graph will be like this: 1 / \ v v 2-->3 Example 2: Input: [[1,2], [2,3], [3,4], [4,1], [1,5]] Output: [4,1] Explanation: The given directed graph will be like this: 5 <- 1 -> 2 ^ | | v 4 <- 3 Example 3: Input: [[2,1],[3,1],[4,2],[1,4]] Output: [2,1] Explanation: The given directed graph will be like this: 1 <- 2 ↑ ↘︎ ↑ 3 4 Note: The size of the input 2D-array will be between 3 and 1000. Every integer represented in the 2D-array will be between 1 and N, where N is the size of the input array.
解法:並查集(Disjoint Set)
與無向圖的區別:
- 父子關係明確
因此,要成為tree,除了不能形成環cycle之外,對每一個節點,還只能有一個parent節點。
例如,
- Example的 1:節點3 就有 兩個parent:1 和 2 ([1,3], [2,3])
- Example的 2:節點1 就有 兩個parent:2 和 3 ([2,1], [3,1])
所以,我們在原先無向圖的求解之前,
增加判斷parent節點個數的邏輯。
這裡增加parent陣列,表示每個節點的父節點。
⚠️ 注意:這裡不是Disjoint Set類裡的表示每個節點root的輔助變數。是本問題特殊增加。
遍歷edges,對每個[u, v],更新parent[v]=u
然後,在每次更新之前,判斷是否parent[v]已經被賦值,若被賦值了,則我們要返回的最終結果,有可能是:
- candidate_res1:之前賦值的那個邊:[parent[v], v]
- candidate_res2:or 當前的邊:[u, v]
這裡說明,節點v有兩個parent。若兩條邊都在,那麼一定不能構成tree。
然後,我們帶著這兩個可能的結果,去嘗試和無向圖一樣的環cycle的判斷邏輯。
我們先假設res2不正確,刪除res2的邊。去進行嘗試。
- 若出現環:
- 證明我們刪錯了,結果應該是res1。
- or 剛才的parent判斷中,根本沒有出現一個節點有兩個parent的情況,那麼直接返回當前出現環的邊。(和無向圖的一樣)
- 若沒有出現環:
- 證明我們刪對了,結果就是res2。
程式碼參考:
1 class Solution { 2 public: 3 vector<int> findRedundantDirectedConnection(vector<vector<int>>& edges) { 4 vector<int> candidate_res1, candidate_res2; 5 vector<int> parent(edges.size(),-1); 6 //find duplicated root point 7 for(int i=0; i<edges.size(); i++) { 8 int p1 = edges[i][0], p2 = edges[i][1]; 9 if(parent[p2-1] != -1) {//p2 has already had a parent 10 candidate_res1 = {parent[p2-1], p2}; 11 candidate_res2 = {p1, p2}; 12 //delete res2 13 edges[i][0] = -1; 14 edges[i][1] = -1; 15 break; 16 } 17 parent[p2-1]=p1; 18 } 19 DisjointSet DS(edges.size()); 20 for(int i=0; i<edges.size(); i++) { 21 int p1 = edges[i][0], p2 = edges[i][1]; 22 if(p1==-1 || p2==-1) continue; 23 if(false == DS.merge(p1-1, p2-1)) {//cycle is found 24 return candidate_res1.empty()?edges[i]:candidate_res1; 25 } 26 } 27 return candidate_res2; 28 } 29 };
附:DisjointSet的程式碼參考:
1 class DisjointSet { 2 public: 3 DisjointSet(int n):parent(n), rank(n, 0) { 4 for(int i=0; i<n; i++) parent[i] = i; 5 } 6 int find(int i) { 7 if(parent[i] != i) { 8 parent[i] = find(parent[i]); 9 } 10 return parent[i]; 11 } 12 bool merge(int x, int y) { 13 int x_root = find(x); 14 int y_root = find(y); 15 if(x_root == y_root) return false; 16 if(rank[x_root] < rank[y_root]) { 17 parent[x_root] = y_root; 18 } else if(rank[y_root] < rank[x_root]) { 19 parent[y_root] = x_root; 20 } else { 21 parent[x_root] = y_root; 22 y_root++; 23 } 24 return true; 25 } 26 private: 27 vector<int> parent; 28 vector<int> rank; 29 };