1. 程式人生 > >[LeetCode] Redundant Connection II 冗餘的連線之二

[LeetCode] Redundant Connection II 冗餘的連線之二

In this problem, a rooted tree is a directed graph such that, there is exactly one node (the root) for which all other nodes are descendants of this node, plus every node has exactly one parent, except for the root node which has no parents.

The given input is a directed graph that started as a rooted tree with N nodes (with distinct values 1, 2, ..., N), with one additional directed edge added. The added edge has two different vertices chosen from 1 to N, and was not an edge that already existed.

The resulting graph is given as a 2D-array of edges. Each element of edges is a pair [u, v] that represents a directed edge connecting nodes u and v, where u is a parent of child v.

Return an edge that can be removed so that the resulting graph is a rooted tree of N nodes. If there are multiple answers, return the answer that occurs last in the given 2D-array.

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

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.

這道題是之前那道Redundant Connection的拓展,那道題給的是無向圖,只需要刪掉組成環的最後一條邊即可,歸根到底就是檢測環就行了。而這道題給我們的是有向圖,那麼整個就複雜多了,因為有多種情況存在,比如給的例子1就是無環,但是有入度為2的結點3。再比如例子2就是有環,但是沒有入度為2的結點。其實還有一種情況例子沒有給出,就是既有環,又有入度為2的結點。好,我們現在就來總結一下這三種情況:

第一種:無環,但是有結點入度為2的結點(結點3)

[[1,2], [1,3], [2,3]]

  1
 / \
v   v
2-->3

第二種:有環,沒有入度為2的結點

[[1,2], [2,3], [3,4], [4,1], [1,5]]

5 <- 1 -> 2
     ^    |
     |    v
     4 <- 3

第三種:有環,且有入度為2的結點(結點1)

[[1,2],[2,3],[3,1],[1,4]]

     4
    /
   v
   1
 /  ^
v    \
2 -->3

對於這三種情況的處理方法各不相同,首先對於第一種情況,我們返回的產生入度為2的後加入的那條邊[2, 3],而對於第二種情況,我們返回的是剛好組成環的最後加入的那條邊[4, 1],最後對於第三種情況我們返回的是組成環,且組成入度為2的那條邊[3, 1]。

明白了這些,我們先來找入度為2的點,如果有的話,那麼我們將當前產生入度為2的後加入的那條邊標記為second,前一條邊標記為first。然後我們來找環,為了方便起見,找環使用聯合查詢Union Find的方法,可參見Redundant Connection中的解法三。當我們找到了環之後,如果first不存在,說明是第二種情況,我們返回剛好組成環的最後加入的那條邊。如果first存在,說明是第三種情況,我們返回first。如果沒有環存在,說明是第一種情況,我們返回second,參見程式碼如下:

class Solution {
public:
    vector<int> findRedundantDirectedConnection(vector<vector<int>>& edges) {
        int n = edges.size();
        vector<int> root(n + 1, 0), first, second;
        for (auto& edge : edges) {
            if (root[edge[1]] == 0) {
                root[edge[1]] = edge[0];
            } else {
                first = {root[edge[1]], edge[1]};
                second = edge;
                edge[1] = 0;
            }
        }
        for (int i = 0; i <= n; ++i) root[i] = i;
        for (auto& edge : edges) {
            if (edge[1] == 0) continue;
            int x = getRoot(root, edge[0]), y = getRoot(root, edge[1]);
            if (x == y) return first.empty() ? edge : first;
            root[x] = y;
        }
        return second;
    }
    int getRoot(vector<int>& root, int i) {
        return i == root[i] ? i : getRoot(root, root[i]);
    }
};

討論:使用聯合查詢Union Find的方法一般都需要寫個子函式,來查詢祖宗結點,上面的解法getRoot()函式就是這個子函式,我們使用遞迴的形式來寫的,其實還可以用迭代的方式來寫,下面這兩種寫法都可以:

int getRoot(vector<int>& root, int i) {
    while (i != root[i]) {
        root[i] = root[root[i]];
        i = root[i];
    }
    return i;
}
int getRoot(vector<int>& root, int i) {
    while (i != root[i]) i = root[i];
    return i;
}

類似題目:

參考資料: