1. 程式人生 > 實用技巧 >685. Redundant Connection II

685. Redundant Connection II

問題:

684. Redundant Connection

的第二版本,由原來的無向圖->有向圖

那麼給定一組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 };