-
Notifications
You must be signed in to change notification settings - Fork 9
/
unionfind.py
108 lines (88 loc) · 2.84 KB
/
unionfind.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
class UnionFind:
"""
Notes:
unionfind data structure specialized for finding hex connections.
Implementation inspired by UAlberta CMPUT 275 2015 class notes.
Attributes:
parent (dict): Each group parent
rank (dict): Each group rank
groups (dict): Stores the groups and chain of cells
ignored (list): The neighborhood of board edges has to be ignored
"""
def __init__(self) -> None:
"""
Initialize parent and rank as empty dictionaries, we will
lazily add items as necessary.
"""
self.parent = {}
self.rank = {}
self.groups = {}
self.ignored = []
def join(self, x, y) -> bool:
"""
Merge the groups of x and y if they were not already,
return False if they were already merged, true otherwise
Args:
x (tuple): game board cell
y (tuple): game board cell
"""
rep_x = self.find(x)
rep_y = self.find(y)
if rep_x == rep_y:
return False
if self.rank[rep_x] < self.rank[rep_y]:
self.parent[rep_x] = rep_y
self.groups[rep_y].extend(self.groups[rep_x])
del self.groups[rep_x]
elif self.rank[rep_x] > self.rank[rep_y]:
self.parent[rep_y] = rep_x
self.groups[rep_x].extend(self.groups[rep_y])
del self.groups[rep_y]
else:
self.parent[rep_x] = rep_y
self.rank[rep_y] += 1
self.groups[rep_y].extend(self.groups[rep_x])
del self.groups[rep_x]
return True
def find(self, x):
"""
Get the representative element associated with the set in
which element x resides. Uses grandparent compression to compression
the tree on each find operation so that future find operations are faster.
Args:
x (tuple): game board cell
"""
if x not in self.parent:
self.parent[x] = x
self.rank[x] = 0
if x in self.ignored:
self.groups[x] = []
else:
self.groups[x] = [x]
px = self.parent[x]
if x == px:
return x
gx = self.parent[px]
if gx == px:
return px
self.parent[x] = gx
return self.find(gx)
def connected(self, x, y) -> bool:
"""
Check if two elements are in the same group.
Args:
x (tuple): game board cell
y (tuple): game board cell
"""
return self.find(x) == self.find(y)
def set_ignored_elements(self, ignore):
"""
Elements in ignored, edges has to be ignored
"""
self.ignored = ignore
def get_groups(self) -> dict:
"""
Returns:
Groups
"""
return self.groups