-
Notifications
You must be signed in to change notification settings - Fork 26
/
union_find.py
120 lines (98 loc) · 3.3 KB
/
union_find.py
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
'''
Created on 06/apr/2013
@author: mlarocca
'''
class UnionFind:
def __init__(self, size):
''' : Constructor :
Create a new union-find data structure
: param size : The initial size of the union-find;
The size can be later increased, but not decreased.
: type size : int (Other types will be converted to int)
: raise IllegalArgumentException : If size is less than 1.
: return : self, as all constructors.
'''
self.n = int(size)
if self.n <= 0:
raise Exception("IllegalArgumentException: size must be positive")
self.set = range(size)
self.set_size = [1 for _ in xrange(size)]
def add_element(self):
''' Add a new element to the union-find; the new element
will be assigned to its own component.
: return : self, to allow method chaining.
'''
self.set.append(self.n)
self.set_size.append(1)
self.n += 1
return self
def find_root(self, i):
'''
Implement find with path compression
: param i : The element whose root has to be found.
: type i : int (Other types will be converted to int)
'''
#makes sure i is an integer
i = int(i)
if self.set[i] != i:
self.set[i] = self.find_root(self.set[i])
return self.set[i]
def connected(self, i, j):
''' Are elements i and j connected?
: param i : The first element to check.
: param j : The second element to check.
: return : True <=> i and j belongs to the same component.
: raise IllegalArgumentException : Raise an exception if either element is not in the union set
'''
if i == j:
if 0 <= i < self.n:
return True
else:
raise Exception("IllegalArgumentException")
root_i = self.find_root(i)
root_j = self.find_root(j)
return root_i == root_j
def union(self, i, j):
''' Perform the union of two components, if they aren't unified yet.
: param i : The first element.
: param j : The second element, to be unified with i's component.
: raise Exception: Raise an exception if either element is not in the
union set (through find_root).
: return : The size of the newly created component
'''
root_i = self.find_root(i)
root_j = self.find_root(j)
if root_i == root_j:
return self.set_size[root_i]
if self.set_size[root_i] <= self.set_size[root_j]:
self.set[root_i] = root_j
self.set_size[root_j] += self.set_size[root_i]
return self.set_size[root_i]
else:
self.set[root_j] = root_i
self.set_size[root_i] += self.set_size[root_j]
return self.set_size[root_j]
def __str__(self):
''' : override :
'''
res = [str(range(self.n)), str(self.set), str(self.set_size)]
return "\n".join(res)
if __name__ == '__main__':
def test_UF():
''' Test the structure '''
u = UnionFind(4)
#print u
assert not u.connected(2, 3)
u.union(2,3)
assert u.connected(2, 3)
u.union(2,1)
assert u.connected(2, 3)
#print u
u.add_element()
assert not u.connected(2,4)
u.union(0,4)
assert u.connected(4,0)
assert not u.connected(2,4)
#print u
#end of test_UF definition
test_UF()