2
2
from collections import namedtuple
3
3
4
4
Node_Degree = namedtuple ('Node_Degree' , ['vertex_no' , 'in_degree' , 'out_degree' ])
5
+ Cycle = namedtuple ('Cycle' , ['cycle_no' , 'cycle_vertex_pos' ])
5
6
6
7
class Graph :
7
8
def __init__ (self , edges ):
@@ -100,7 +101,7 @@ def _is_eulerian_graph(self):
100
101
101
102
return True
102
103
103
- def form_cycle (self , start , cycle , visited_edge_indexes , unvisited_edge_node_dict ):
104
+ def form_cycle_naive (self , start , cycle , visited_edge_indexes , unvisited_edge_node_dict ):
104
105
105
106
node = start
106
107
while visited_edge_indexes [node ] + 1 < len (self .adjacency_list [node ]):
@@ -121,7 +122,7 @@ def form_cycle(self, start, cycle, visited_edge_indexes, unvisited_edge_node_dic
121
122
122
123
return - 1 if len (unvisited_edge_node_dict ) == 0 else next (iter (unvisited_edge_node_dict .values ()))
123
124
124
- def eulerian_cycle (self ):
125
+ def eulerian_cycle_naive (self ):
125
126
126
127
assert (self ._is_eulerian_graph ())
127
128
@@ -131,7 +132,7 @@ def eulerian_cycle(self):
131
132
# nodes with unvisited edges: {node, position in cycle}
132
133
unvisited_edge_node_dict = dict ()
133
134
134
- new_start_pos_in_cycle = self .form_cycle (0 , cycle , visited_edge_indexes , unvisited_edge_node_dict )
135
+ new_start_pos_in_cycle = self .form_cycle_naive (0 , cycle , visited_edge_indexes , unvisited_edge_node_dict )
135
136
136
137
while new_start_pos_in_cycle != - 1 :
137
138
@@ -145,13 +146,86 @@ def eulerian_cycle(self):
145
146
else :
146
147
unvisited_edge_node_dict [node ] += (len (cycle ) - new_start_pos_in_cycle )
147
148
148
- new_start_pos_in_cycle = self .form_cycle (new_start , cycle , visited_edge_indexes , unvisited_edge_node_dict )
149
+ new_start_pos_in_cycle = self .form_cycle_naive (new_start , cycle , visited_edge_indexes , unvisited_edge_node_dict )
149
150
150
151
return cycle
151
152
153
+ def form_cycle (self , start , visited_edge_indexes , unvisited_edge_node_set ):
154
+
155
+ node = start
156
+ cycle = []
157
+ while visited_edge_indexes [node ] + 1 < len (self .adjacency_list [node ]):
158
+
159
+ cycle .append (node )
160
+
161
+ # Save this node, if there are 2 or more unvisited edges
162
+ if (visited_edge_indexes [node ] + 2 ) < len (self .adjacency_list [node ]):
163
+ if not node in unvisited_edge_node_set :
164
+ unvisited_edge_node_set .add (node )
165
+
166
+ elif node in unvisited_edge_node_set :
167
+ unvisited_edge_node_set .remove (node )
168
+
169
+ visited_edge_indexes [node ] += 1
170
+ node = self .adjacency_list [node ][ visited_edge_indexes [node ] ]
171
+
172
+ cycle .append (node )
173
+
174
+ return cycle , - 1 if len (unvisited_edge_node_set ) == 0 else next (iter (unvisited_edge_node_set ))
175
+
176
+ # Running Time: O(|E|)
177
+ def eulerian_cycle (self ):
178
+
179
+ assert (self ._is_eulerian_graph ())
180
+
181
+ visited_edge_indexes = [- 1 for _ in range (len (self .nodes ))]
182
+
183
+ # nodes with unvisited edges: {node, position in cycle}
184
+ unvisited_edge_node_set = set ()
185
+
186
+ # {node_no, cycle_no []}: will contain all cycle no starting from aach node
187
+ node_cycles_dict = dict ()
188
+
189
+ # 1. Find all cycles
190
+ cycles = []
191
+ start_node = 0
192
+ while start_node != - 1 :
193
+
194
+ cycle , start_node = self .form_cycle (start_node , visited_edge_indexes , unvisited_edge_node_set )
195
+
196
+ cycles .append (cycle )
197
+
198
+ if cycle [0 ] in node_cycles_dict :
199
+ node_cycles_dict [ cycle [0 ] ].append (len (cycles ) - 1 )
200
+ else :
201
+ node_cycles_dict [ cycle [0 ] ] = [ len (cycles ) - 1 ]
202
+
203
+ # 2. Build Eulerian Cycle:
204
+ cycle = []
205
+ cycle_queue = [ Cycle (0 , 0 ) ]
206
+ while len (cycle_queue ) != 0 :
207
+
208
+ (cycle_no , cycle_vertex_pos ) = cycle_queue .pop ()
209
+ vertex_no = cycles [cycle_no ][cycle_vertex_pos ]
210
+
211
+ cycle .append (vertex_no )
212
+ cycle_vertex_pos += 1
213
+ if cycle_vertex_pos < len (cycles [cycle_no ]):
214
+ cycle_queue .append ((cycle_no , cycle_vertex_pos ))
215
+
216
+ if vertex_no in node_cycles_dict :
217
+ for new_cycle_no in node_cycles_dict [vertex_no ]:
218
+ if new_cycle_no != cycle_no :
219
+ cycle_queue .append ((new_cycle_no , 1 ))
220
+
221
+ node_cycles_dict .pop (vertex_no )
222
+
223
+ return cycle
224
+
152
225
if __name__ == "__main__" :
153
226
edges = sys .stdin .read ().strip ().splitlines ()
154
227
155
228
eulerian_graph = Graph (edges )
156
229
157
- print ('->' .join ( [ eulerian_graph .nodes [node ] for node in eulerian_graph .eulerian_cycle () ] ))
230
+ print ("Naive : " , '->' .join ( [ eulerian_graph .nodes [node ] for node in eulerian_graph .eulerian_cycle_naive () ] ))
231
+ print ("Efficient: " , '->' .join ( [ eulerian_graph .nodes [node ] for node in eulerian_graph .eulerian_cycle () ] ))
0 commit comments