-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathdynamic_connectivity.py
205 lines (156 loc) · 6.5 KB
/
dynamic_connectivity.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
class ConnectedComponents:
"""
From a set of known vertices and edges (merge_list)
adds or removes vertices to graph and keeps track of
connected components dynamically.
"""
def __init__(self, merge_list):
"""
Initialize graph and connected components with merge_list.
All vertices in graph are filtered and connected components
are empty.
merge_list = list of list with integer ids of vertices
e.g. merge_list = [[1,2,3],[0],[0],[0]]
"""
self.merge_list = merge_list
# Set component key to minimum vertex value in component
self.components = {}
self.vertices = {}
n_vertices = len(self.merge_list)
def addVertex(self, vertex):
if vertex in self.vertices:
comp_add = {}
comp_remove = {}
else:
# Find all adjacent components in current solution
other_components = set()
for neighbor in self.merge_list[vertex]: #TODO: Better get neighbors from filtered graph?
if neighbor in self.vertices:
oc = self.vertices[neighbor]
other_components.add(oc)
# Not connected to other components
# -> Create new component
if len(other_components) == 0:
# New component id
new_id = vertex
# Add new vertex
self.vertices[vertex] = new_id
self.components[new_id] = [vertex]
comp_add = {new_id: [vertex]}
comp_remove = {}
# Connected to other components
else:
# New component id
new_id = min(other_components)
new_id = min(new_id, vertex)
# Get all vertices and remove old component
comp_remove = {}
new_v_list = [vertex]
for c in other_components:
v_list = self.components.pop(c)
comp_remove[c] = list(v_list) # save copy of original list
if len(new_v_list) == 1: # Fist iteration, i.e. new_v_list == [vertex]
if v_list[-1] in self.merge_list[vertex]:
new_v_list = v_list + new_v_list
else:
new_v_list = v_list[::-1] + new_v_list # no need to copy?
else: # Second iteration, if any
if v_list[0] in self.merge_list[vertex]:
new_v_list = new_v_list + v_list
else:
new_v_list = new_v_list + v_list[::-1]
for v in new_v_list:
self.vertices[v] = new_id
self.components[new_id] = new_v_list
comp_add = {new_id: list(new_v_list)} # save copy of original list
return comp_add, comp_remove
def removeVertex(self, vertex):
if vertex not in self.vertices:
comp_add = {}
comp_remove = {}
else:
# Get component id/ remove from vertices
comp_id = self.vertices.pop(vertex)
size_component = len(self.components[comp_id])
# Just remove component
if size_component == 1:
self.components.pop(comp_id)
comp_add = {}
comp_remove = {comp_id: [vertex]}
# Create new components
else:
# Get neighbors in graph (must be len=1 or len=2)
v_list = self.components[comp_id]
index = v_list.index(vertex)
# If is terminal vertex
# No need for finding connected component
if index == 0 or index == size_component-1:
comp_remove = {comp_id: list(self.components[comp_id])}
# Remove vertex from component
self.components[comp_id].remove(vertex)
new_comp_id = min(self.components[comp_id])
self.components[new_comp_id] = self.components.pop(comp_id)
for v in self.components[new_comp_id]:
self.vertices[v] = new_comp_id
comp_add = {new_comp_id: list(self.components[new_comp_id])}
# Else find new connected components
else:
comp_remove = {comp_id: list(self.components[comp_id])}
comp_add = {}
self.components.pop(comp_id)
# Left component
v_ids = v_list[0:index]
new_comp_id = min(v_ids)
self.components[new_comp_id] = v_ids
for v in v_ids:
self.vertices[v] = new_comp_id
comp_add[new_comp_id] = list(v_ids)
# Right component
v_ids = v_list[index+1:]
new_comp_id = min(v_ids)
self.components[new_comp_id] = v_ids
for v in v_ids:
self.vertices[v] = new_comp_id
comp_add[new_comp_id] = list(v_ids)
return comp_add, comp_remove
if __name__ == '__main__':
merge_list = [[1],[0,2],[1,3],[2,4],[3,5],[4],[7],[6],[]]
cc = ConnectedComponents(merge_list)
cc.addVertex(0)
cc.addVertex(1)
cc.addVertex(2)
cc.addVertex(3)
cc.addVertex(4)
cc.addVertex(5)
cc.addVertex(6)
cc.addVertex(7)
cc.addVertex(8)
print("Graph:")
print(cc.components)
print(cc.vertices)
print(" ")
print("Remove vertex 2")
comp_add, comp_remove = cc.removeVertex(2)
print(comp_add, comp_remove)
print(cc.components)
print(cc.vertices)
print(" ")
print("Remove vertex 6")
comp_add, comp_remove = cc.removeVertex(6)
print(comp_add, comp_remove)
print(cc.components)
print(cc.vertices)
print(" ")
print("Remove vertex 8")
comp_add, comp_remove = cc.removeVertex(8)
print(comp_add, comp_remove)
print(cc.components)
print(cc.vertices)
print(" ")
print("Add vertex 2")
comp_add, comp_remove = cc.addVertex(2)
print(comp_add, comp_remove)
print(cc.components)
print(cc.vertices)