In this problem, a tree is an undirected graph that is connected and has no cycles.
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.
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
.
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
.
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:
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:
- 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.
這道題給我們了一個無向圖,讓刪掉組成環的最后一條邊,其實這道題跟之前那道 Graph Valid Tree 基本沒什么區別,三種解法都基本相同。博主覺得老題稍微變一下就是一道新題,而 onsite 遇到原題的概率很小,大多情況下都會稍稍變一下,所以舉一反三的能力真的很重要,要完全吃透一道題也不太容易,需要多下功夫。首先來看遞歸的解法,這種解法的思路是,每加入一條邊,就進行環檢測,一旦發現了環,就返回當前邊。對於無向圖,還是用鄰接表來保存,建立每個結點和其所有鄰接點的映射,由於兩個結點之間不算有環,所以要避免這種情況 1->{2}, 2->{1} 的死循環,用一個變量 pre 記錄上一次遞歸的結點,比如上一次遍歷的是結點1,那么在遍歷結點2的鄰接表時,就不會再次進入結點1了,這樣有效的避免了死循環,使其能返回正確的結果,參見代碼如下:
解法一:
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
類似題目:
Number of Connected Components in an Undirected Graph
參考資料:
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