[LeetCode] 684. Redundant Connection 冗餘的鏈接

 

In this problem, a tree is an undirected graph that is connected and has no cycles.html

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

The resulting graph is given as a 2D-array of edges. Each element of edges is a pair [u, v] with u < v, that represents an undirected edge connecting nodes u and v.git

Return an edge that can be removed so that the resulting graph is a tree of N nodes. If there are multiple answers, return the answer that occurs last in the given 2D-array. The answer edge [u, v] should be in the same format, with u < v.github

Example 1:數組

Input: [[1,2], [1,3], [2,3]]
Output: [2,3]
Explanation: The given undirected graph will be like this:
  1
 / \
2 - 3

 

Example 2:post

Input: [[1,2], [2,3], [3,4], [1,4], [1,5]]
Output: [1,4]
Explanation: The given undirected graph will be like this:
5 - 1 - 2
    |   |
    4 - 3

 

Note:this

  • 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.

 

Update (2017-09-26):
We have overhauled the problem description + test cases and specified clearly the graph is an undirected graph. For the directedgraph follow up please see Redundant Connection II). We apologize for any inconvenience caused.url

 

這道題給咱們了一個無向圖,讓刪掉組成環的最後一條邊,其實這道題跟以前那道 Graph Valid Tree 基本沒什麼區別,三種解法都基本相同。博主以爲老題稍微變一下就是一道新題,而 onsite 遇到原題的機率很小,大多狀況下都會稍稍變一下,因此觸類旁通的能力真的很重要,要徹底吃透一道題也不太容易,須要多下功夫。首先來看遞歸的解法,這種解法的思路是,每加入一條邊,就進行環檢測,一旦發現了環,就返回當前邊。對於無向圖,仍是用鄰接表來保存,創建每一個結點和其全部鄰接點的映射,因爲兩個結點之間不算有環,因此要避免這種狀況 1->{2}, 2->{1} 的死循環,用一個變量 pre 記錄上一次遞歸的結點,好比上一次遍歷的是結點1,那麼在遍歷結點2的鄰接表時,就不會再次進入結點1了,這樣有效的避免了死循環,使其能返回正確的結果,參見代碼以下:spa

 

解法一:code

class Solution {
public:
    vector<int> findRedundantConnection(vector<vector<int>>& edges) {
        unordered_map<int, unordered_set<int>> m;
        for (auto edge : edges) {
            if (hasCycle(edge[0], edge[1], m, -1)) return edge;
            m[edge[0]].insert(edge[1]);
            m[edge[1]].insert(edge[0]);
        }
        return {};
    }
    bool hasCycle(int cur, int target, unordered_map<int, unordered_set<int>>& m, int pre) {
        if (m[cur].count(target)) return true;
        for (int num : m[cur]) {
            if (num == pre) continue;
            if (hasCycle(num, target, m, cur)) return true;
        }
        return false;
    }
};

 

既然遞歸能作,通常來講迭代也木有問題。可是因爲 BFS 的遍歷機制和 DFS 不一樣,因此無法採用利用變量 pre 來避免上面所說的死循環(不是很肯定,多是博主沒想出來,有作出來的請在評論區貼上代碼),因此採用一個集合來記錄遍歷過的結點,若是該結點已經遍歷過了,那麼直接跳過便可,不然就把該結點加入 queue 和集合,繼續循環,參見代碼以下:

 

解法二:

class Solution {
public:
    vector<int> findRedundantConnection(vector<vector<int>>& edges) {
        unordered_map<int, unordered_set<int>> m;
        for (auto edge : edges) {
            queue<int> q{{edge[0]}};
            unordered_set<int> s{{edge[0]}};
            while (!q.empty()) {
                auto t = q.front(); q.pop();
                if (m[t].count(edge[1])) return edge;
                for (int num : m[t]) {
                    if (s.count(num)) continue;
                    q.push(num);
                    s.insert(num);
                }
            }
            m[edge[0]].insert(edge[1]);
            m[edge[1]].insert(edge[0]);
        }
        return {};
    }
};

 

其實這道題最好的解法使用 Union Find 來作,論壇上清一色的都是用這種解法來作的,像博主用 DFS 和 BFS 這麼清新脫俗的方法還真很少:) 其實 Union Find 的核心思想並非很難理解,首先創建一個長度爲 (n+1) 的數組 root,因爲這道題並無明確的說明n是多少,只是說了輸入的二位數組的長度不超過 1000,那麼n絕對不會超過 2000,加1的緣由是因爲結點值是從1開始的,而數組是從0開始的,懶得轉換了,就多加一位得了。將這個數組都初始化爲 -1,有些人喜歡初始化爲i,均可以。開始表示每一個結點都是一個單獨的組,所謂的 Union Find 就是要讓結點之間創建關聯,好比若 root[1] = 2,就表示結點1和結點2是相連的,root[2] = 3 表示結點2和結點3是相連的,若是此時新加一條邊 [1, 3] 的話,咱們經過 root[1] 獲得2,再經過 root[2] 獲得3,說明結點1有另外一條路徑能到結點3,這樣就說明環是存在的;若是沒有這條路徑,那麼要將結點1和結點3關聯起來,讓 root[1] = 3 便可,參見代碼以下:

 

解法三:

class Solution {
public:
    vector<int> findRedundantConnection(vector<vector<int>>& edges) {
        vector<int> root(2001, -1);
        for (auto edge : edges) {
            int x = find(root, edge[0]), y = find(root, edge[1]);
            if (x == y) return edge;
            root[x] = y;
        }
        return {};
    }
    int find(vector<int>& root, int i) {
        while (root[i] != -1) {
            i = root[i];
        }
        return i;
    }
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/684

 

相似題目:

Friend Circles

Accounts Merge

Redundant Connection II

Number of Islands II

Graph Valid Tree

Number of Connected Components in an Undirected Graph

Similar String Groups

 

參考資料:

https://leetcode.com/problems/redundant-connection/

https://leetcode.com/problems/redundant-connection/discuss/112562/My-DFS-and-BSF-solutions

https://leetcode.com/problems/redundant-connection/discuss/107984/10-line-Java-solution-Union-Find.

https://leetcode.com/problems/redundant-connection/discuss/108010/C%2B%2B-solution-using-union-find

 

LeetCode All in One 題目講解彙總(持續更新中...)

相關文章
相關標籤/搜索