https://leetcode.com/problems/redundant-connection/description/
Problem:
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:
1 2 3 4 5 6 |
Input: [[1,2], [1,3], [2,3]] Output: [2,3] Explanation: The given undirected graph will be like this: 1 / \ 2 - 3 |
Example 2:
1 2 3 4 5 6 |
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.
Idea:
DFS / Union-Find
Solutions:
C++ / DFS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// Author: Huahua // Time Complexity: O(n^2) // Running Time: 22 ms class Solution { public: vector<int> findRedundantConnection(vector<vector<int>>& edges) { unordered_map<int, vector<int>> graph; for (const auto& edge : edges) { int u = edge[0]; int v = edge[1]; unordered_set<int> visited; if (hasPath(u, v, graph, visited)) return edge; graph[u].push_back(v); graph[v].push_back(u); } return {}; } private: bool hasPath(int curr, int goal, const unordered_map<int, vector<int>>& graph, unordered_set<int>& visited) { if (curr == goal) return true; visited.insert(curr); if (!graph.count(curr) || !graph.count(goal)) return false; for (int next : graph.at(curr)) { if (visited.count(next)) continue; if (hasPath(next, goal, graph, visited)) return true; } return false; } }; |
C++ / Union Find
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
// Author: Huahua // Running time: 6 ms class UnionFindSet { public: UnionFindSet(int n) { ranks_ = vector<int>(n + 1, 0); parents_ = vector<int>(n + 1, 0); for (int i = 0; i < parents_.size(); ++i) parents_[i] = i; } // Merge sets that contains u and v. // Return true if merged, false if u and v are already in one set. bool Union(int u, int v) { int pu = Find(u); int pv = Find(v); if (pu == pv) return false; // Meger low rank tree into high rank tree if (ranks_[pv] > ranks_[pu]) parents_[pu] = pv; else if (ranks_[pu] > ranks_[pv]) parents_[pv] = pu; else { parents_[pv] = pu; ranks_[pv] += 1; } return true; } // Get the root of u. int Find(int u) { // Compress the path during traversal if (u != parents_[u]) parents_[u] = Find(parents_[u]); return parents_[u]; } private: vector<int> parents_; vector<int> ranks_; }; class Solution { public: vector<int> findRedundantConnection(vector<vector<int>>& edges) { UnionFindSet s(edges.size()); for(const auto& edge: edges) if (!s.Union(edge[0], edge[1])) return edge; return {}; } }; |
Java / Union Find
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
// Author: Huahua // Runtime: 6 ms class Solution { class UnionFindSet { private int[] parents_; private int[] ranks_; public UnionFindSet(int n) { parents_ = new int[n + 1]; ranks_ = new int[n + 1]; for (int i = 0; i < parents_.length; ++i) { parents_[i] = i; ranks_[i] = 1; } } public boolean Union(int u, int v) { int pu = Find(u); int pv = Find(v); if (pu == pv) return false; if (ranks_[pv] > ranks_[pu]) parents_[pu] = pv; else if (ranks_[pu] > ranks_[pv]) parents_[pv] = pu; else { parents_[pv] = pu; ranks_[pu] += 1; } return true; } public int Find(int u) { while (parents_[u] != u) { parents_[u] = parents_[parents_[u]]; u = parents_[u]; } return u; } } public int[] findRedundantConnection(int[][] edges) { UnionFindSet s = new UnionFindSet(edges.length); for (int[] edge : edges) if (!s.Union(edge[0], edge[1])) return edge; return null; } } |
Python: Union Find
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
""" Author: Huahua Running Time: 66 ms """ class Solution: def findRedundantConnection(self, edges): p = [0]*(len(edges) + 1) s = [1]*(len(edges) + 1) def find(u): while p[u] != u: p[u] = p[p[u]] u = p[u] return u for u, v in edges: if p[u] == 0: p[u] = u if p[v] == 0: p[v] = v pu, pv = find(u), find(v) if pu == pv: return [u, v] if s[pv] > s[pu]: u, v = v, u p[pv] = pu s[pu] += s[pv] return [] |
Python / Union Find V2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
""" Author: Huahua Runtime: 59 ms (<92.42%) """ class UnionFindSet: def __init__(self, n): self._parents = [i for i in range(n + 1)] self._ranks = [1 for i in range(n + 1)] def find(self, u): while u != self._parents[u]: self._parents[u] = self._parents[self._parents[u]] u = self._parents[u] return u def union(self, u, v): pu, pv = self.find(u), self.find(v) if pu == pv: return False if self._ranks[pu] < self._ranks[pv]: self._parents[pu] = pv elif self._ranks[pu] > self._ranks[pv]: self._parents[pv] = pu else: self._parents[pv] = pu self._ranks[pu] += 1 return True class Solution: def findRedundantConnection(self, edges): s = UnionFindSet(len(edges)) for edge in edges: if not s.union(edge[0], edge[1]): return edge return None ndSet(len(edges)) for edge in edges: if not s.union(edge[0], edge[1]): return edge return None |