Skip to content

Commit 0441347

Browse files
author
Chris Wu
committed
no message
1 parent 8fe6976 commit 0441347

File tree

1 file changed

+85
-27
lines changed

1 file changed

+85
-27
lines changed

problems/redundant-connection.py

Lines changed: 85 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,95 @@
1+
#DFS
12
"""
2-
We use the `roots` to store the root of each node.
3-
For example, at index 1 value is 5, means that node1's root is node5.
3+
For each edge (u, v), traverse the graph with a depth-first search to see if we can connect u to v. If we can, then it must be the duplicate edge.
4+
Time Complexity: O(N^2)
5+
Space Complexity: O(N)
6+
"""
7+
from collections import defaultdict
48

5-
We use `find()` to find the node's root. In other words, the node's parent's parent's ...
6-
Every node we encounter along the way, we update their root value in `roots` to the up most root. (This technique is called path compression).
9+
class Solution(object):
10+
def findRedundantConnection(self, edges):
11+
def dfs(u, v):
12+
seen = set()
13+
stack = []
14+
stack.append(u)
15+
16+
while stack:
17+
node = stack.pop()
18+
seen.add(node)
19+
20+
if v in G[node]: return True
21+
22+
for nei in G[node]:
23+
if nei not in seen:
24+
stack.append(nei)
25+
return False
26+
27+
G = defaultdict(set)
28+
29+
for u, v in edges:
30+
if u in G and v in G and dfs(u, v): return u, v
31+
G[u].add(v)
32+
G[v].add(u)
733

8-
So, for every edge, we unify `u` and `v` them. #[1]
9-
Which means u and v and all of their parents all lead to one root.
34+
#Disjoint Set Union
35+
"""
36+
For Disjoint Set Union, see
37+
https://www.youtube.com/watch?v=ID00PMy0-vE
38+
Time Complexity: O(N)
39+
Space Complexity: O(N)
40+
"""
41+
class DSU(object):
42+
def __init__(self):
43+
self.parant = range(1001)
44+
self.rank = [0]*1001
45+
46+
def find(self, x):
47+
if self.parant[x]!=x:
48+
self.parant[x] = self.find(self.parant[x])
49+
return self.parant[x]
1050

11-
If u's root (`ur`) and v's root (`vr`) are already the same before we unify them.
12-
This edge is redundant.
51+
def union(self, x, y):
52+
xr, yr = self.find(x), self.find(y)
53+
if xr==yr:
54+
return False
55+
elif self.rank[xr]>self.rank[yr]:
56+
self.parant[yr] = xr
57+
self.rank[xr] += 1
58+
else:
59+
self.parant[xr] = yr
60+
self.rank[yr] += 1
61+
return True
1362

14-
This algorithm is called Union Find, often used in undirected graph cycle detect or grouping.
15-
If you wanted to detect cycles in directed graph, you need to use Topological sort.
16-
"""
1763
class Solution(object):
1864
def findRedundantConnection(self, edges):
19-
def find(x):
20-
if x != roots[x]:
21-
roots[x] = find(roots[x])
22-
return roots[x]
65+
dsu = DSU()
66+
for edge in edges:
67+
if not dsu.union(*edge):
68+
return edge
2369

24-
opt = []
25-
roots = range(len(edges))
26-
27-
for u, v in edges:
28-
# union
29-
ur = find(u)
30-
vr = find(v)
70+
#Disjoint Set Union without Ranking
71+
"""
72+
Time Complexity: O(N)
73+
Space Complexity: O(N)
74+
"""
75+
class DSU(object):
76+
def __init__(self):
77+
self.parant = range(1001)
78+
79+
def find(self, x):
80+
if self.parant[x]!=x:
81+
self.parant[x] = self.find(self.parant[x])
82+
return self.parant[x]
3183

32-
if ur == vr: #[2]
33-
opt = [u, v]
34-
else:
35-
roots[vr] = ur #[1]
84+
def union(self, x, y):
85+
xr, yr = self.find(x), self.find(y)
86+
if xr==yr: return False
87+
self.parant[yr] = xr
88+
return True
3689

37-
return opt
90+
class Solution(object):
91+
def findRedundantConnection(self, edges):
92+
dsu = DSU()
93+
for edge in edges:
94+
if not dsu.union(*edge):
95+
return edge

0 commit comments

Comments
 (0)