@@ -11,15 +11,16 @@ pub trait DetectCycle {
11
11
fn undirected_graph_detect_cycle_dfs < ' a > (
12
12
graph : & ' a UndirectedGraph ,
13
13
visited_node : & mut HashSet < & ' a String > ,
14
- parent : & ' a String ,
14
+ parent : Option < & ' a String > ,
15
15
u : & ' a String ,
16
16
) -> bool {
17
17
visited_node. insert ( u) ;
18
18
for ( v, _) in graph. adjacency_table ( ) . get ( u) . unwrap ( ) {
19
- if v == parent {
19
+ if matches ! ( parent , Some ( parent ) if v == parent) {
20
20
continue ;
21
21
}
22
- if visited_node. contains ( v) || undirected_graph_detect_cycle_dfs ( graph, visited_node, u, v)
22
+ if visited_node. contains ( v)
23
+ || undirected_graph_detect_cycle_dfs ( graph, visited_node, Some ( u) , v)
23
24
{
24
25
return true ;
25
26
}
@@ -36,19 +37,19 @@ fn undirected_graph_detect_cycle_bfs<'a>(
36
37
visited_node. insert ( u) ;
37
38
38
39
// Initialize the queue for BFS, storing (current node, parent node) tuples
39
- let mut queue = VecDeque :: < ( & String , & String ) > :: new ( ) ;
40
- queue. push_back ( ( u, u ) ) ;
40
+ let mut queue = VecDeque :: < ( & String , Option < & String > ) > :: new ( ) ;
41
+ queue. push_back ( ( u, None ) ) ;
41
42
42
43
while let Some ( ( u, parent) ) = queue. pop_front ( ) {
43
44
for ( v, _) in graph. adjacency_table ( ) . get ( u) . unwrap ( ) {
44
- if v == parent {
45
+ if matches ! ( parent , Some ( parent ) if v == parent) {
45
46
continue ;
46
47
}
47
48
if visited_node. contains ( v) {
48
49
return true ;
49
50
}
50
51
visited_node. insert ( v) ;
51
- queue. push_back ( ( v, u ) ) ;
52
+ queue. push_back ( ( v, Some ( u ) ) ) ;
52
53
}
53
54
}
54
55
false
@@ -60,7 +61,7 @@ impl DetectCycle for UndirectedGraph {
60
61
let adj = self . adjacency_table ( ) ;
61
62
for u in adj. keys ( ) {
62
63
if !visited_node. contains ( u)
63
- && undirected_graph_detect_cycle_dfs ( self , & mut visited_node, u , u)
64
+ && undirected_graph_detect_cycle_dfs ( self , & mut visited_node, None , u)
64
65
{
65
66
return true ;
66
67
}
@@ -166,56 +167,128 @@ impl DetectCycle for DirectedGraph {
166
167
167
168
#[ cfg( test) ]
168
169
mod test {
169
- use crate :: data_structures:: { graph:: Graph , DirectedGraph , UndirectedGraph } ;
170
-
171
170
use super :: DetectCycle ;
172
-
173
- #[ test]
174
- fn test_detect_cycle_in_undirected_graph ( ) {
175
- let mut graph_with_cycle = UndirectedGraph :: new ( ) ;
176
-
177
- graph_with_cycle. add_edge ( ( "a" , "b" , 1 ) ) ;
178
- graph_with_cycle. add_edge ( ( "a" , "c" , 1 ) ) ;
179
- graph_with_cycle. add_edge ( ( "b" , "c" , 1 ) ) ;
180
- graph_with_cycle. add_edge ( ( "b" , "d" , 1 ) ) ;
181
- graph_with_cycle. add_edge ( ( "c" , "d" , 1 ) ) ;
182
-
183
- assert ! ( graph_with_cycle. detect_cycle_dfs( ) ) ;
184
- assert ! ( graph_with_cycle. detect_cycle_bfs( ) ) ;
185
-
186
- let mut graph_without_cycle = UndirectedGraph :: new ( ) ;
187
-
188
- graph_without_cycle. add_edge ( ( "a" , "b" , 1 ) ) ;
189
- graph_without_cycle. add_edge ( ( "a" , "c" , 1 ) ) ;
190
- graph_without_cycle. add_edge ( ( "b" , "d" , 1 ) ) ;
191
- graph_without_cycle. add_edge ( ( "c" , "e" , 1 ) ) ;
192
-
193
- assert ! ( !graph_without_cycle. detect_cycle_dfs( ) ) ;
194
- assert ! ( !graph_without_cycle. detect_cycle_bfs( ) ) ;
171
+ use crate :: data_structures:: { graph:: Graph , DirectedGraph , UndirectedGraph } ;
172
+ fn get_undirected_single_node_with_loop ( ) -> UndirectedGraph {
173
+ let mut res = UndirectedGraph :: new ( ) ;
174
+ res. add_edge ( ( "a" , "a" , 1 ) ) ;
175
+ res
195
176
}
196
-
197
- #[ test]
198
- fn test_detect_cycle_in_directed_graph ( ) {
199
- let mut graph_with_cycle = DirectedGraph :: new ( ) ;
200
-
201
- graph_with_cycle. add_edge ( ( "b" , "a" , 1 ) ) ;
202
- graph_with_cycle. add_edge ( ( "c" , "a" , 1 ) ) ;
203
- graph_with_cycle. add_edge ( ( "b" , "c" , 1 ) ) ;
204
- graph_with_cycle. add_edge ( ( "c" , "d" , 1 ) ) ;
205
- graph_with_cycle. add_edge ( ( "d" , "b" , 1 ) ) ;
206
-
207
- assert ! ( graph_with_cycle. detect_cycle_dfs( ) ) ;
208
- assert ! ( graph_with_cycle. detect_cycle_bfs( ) ) ;
209
-
210
- let mut graph_without_cycle = DirectedGraph :: new ( ) ;
211
-
212
- graph_without_cycle. add_edge ( ( "b" , "a" , 1 ) ) ;
213
- graph_without_cycle. add_edge ( ( "c" , "a" , 1 ) ) ;
214
- graph_without_cycle. add_edge ( ( "b" , "c" , 1 ) ) ;
215
- graph_without_cycle. add_edge ( ( "c" , "d" , 1 ) ) ;
216
- graph_without_cycle. add_edge ( ( "b" , "d" , 1 ) ) ;
217
-
218
- assert ! ( !graph_without_cycle. detect_cycle_dfs( ) ) ;
219
- assert ! ( !graph_without_cycle. detect_cycle_bfs( ) ) ;
177
+ fn get_directed_single_node_with_loop ( ) -> DirectedGraph {
178
+ let mut res = DirectedGraph :: new ( ) ;
179
+ res. add_edge ( ( "a" , "a" , 1 ) ) ;
180
+ res
181
+ }
182
+ fn get_undirected_two_nodes_connected ( ) -> UndirectedGraph {
183
+ let mut res = UndirectedGraph :: new ( ) ;
184
+ res. add_edge ( ( "a" , "b" , 1 ) ) ;
185
+ res
186
+ }
187
+ fn get_directed_two_nodes_connected ( ) -> DirectedGraph {
188
+ let mut res = DirectedGraph :: new ( ) ;
189
+ res. add_edge ( ( "a" , "b" , 1 ) ) ;
190
+ res. add_edge ( ( "b" , "a" , 1 ) ) ;
191
+ res
192
+ }
193
+ fn get_directed_two_nodes ( ) -> DirectedGraph {
194
+ let mut res = DirectedGraph :: new ( ) ;
195
+ res. add_edge ( ( "a" , "b" , 1 ) ) ;
196
+ res
197
+ }
198
+ fn get_undirected_triangle ( ) -> UndirectedGraph {
199
+ let mut res = UndirectedGraph :: new ( ) ;
200
+ res. add_edge ( ( "a" , "b" , 1 ) ) ;
201
+ res. add_edge ( ( "b" , "c" , 1 ) ) ;
202
+ res. add_edge ( ( "c" , "a" , 1 ) ) ;
203
+ res
204
+ }
205
+ fn get_directed_triangle ( ) -> DirectedGraph {
206
+ let mut res = DirectedGraph :: new ( ) ;
207
+ res. add_edge ( ( "a" , "b" , 1 ) ) ;
208
+ res. add_edge ( ( "b" , "c" , 1 ) ) ;
209
+ res. add_edge ( ( "c" , "a" , 1 ) ) ;
210
+ res
211
+ }
212
+ fn get_undirected_triangle_with_tail ( ) -> UndirectedGraph {
213
+ let mut res = get_undirected_triangle ( ) ;
214
+ res. add_edge ( ( "c" , "d" , 1 ) ) ;
215
+ res. add_edge ( ( "d" , "e" , 1 ) ) ;
216
+ res. add_edge ( ( "e" , "f" , 1 ) ) ;
217
+ res. add_edge ( ( "g" , "h" , 1 ) ) ;
218
+ res
219
+ }
220
+ fn get_directed_triangle_with_tail ( ) -> DirectedGraph {
221
+ let mut res = get_directed_triangle ( ) ;
222
+ res. add_edge ( ( "c" , "d" , 1 ) ) ;
223
+ res. add_edge ( ( "d" , "e" , 1 ) ) ;
224
+ res. add_edge ( ( "e" , "f" , 1 ) ) ;
225
+ res. add_edge ( ( "g" , "h" , 1 ) ) ;
226
+ res
227
+ }
228
+ fn get_undirected_graph_with_cycle ( ) -> UndirectedGraph {
229
+ let mut res = UndirectedGraph :: new ( ) ;
230
+ res. add_edge ( ( "a" , "b" , 1 ) ) ;
231
+ res. add_edge ( ( "a" , "c" , 1 ) ) ;
232
+ res. add_edge ( ( "b" , "c" , 1 ) ) ;
233
+ res. add_edge ( ( "b" , "d" , 1 ) ) ;
234
+ res. add_edge ( ( "c" , "d" , 1 ) ) ;
235
+ res
236
+ }
237
+ fn get_undirected_graph_without_cycle ( ) -> UndirectedGraph {
238
+ let mut res = UndirectedGraph :: new ( ) ;
239
+ res. add_edge ( ( "a" , "b" , 1 ) ) ;
240
+ res. add_edge ( ( "a" , "c" , 1 ) ) ;
241
+ res. add_edge ( ( "b" , "d" , 1 ) ) ;
242
+ res. add_edge ( ( "c" , "e" , 1 ) ) ;
243
+ res
244
+ }
245
+ fn get_directed_graph_with_cycle ( ) -> DirectedGraph {
246
+ let mut res = DirectedGraph :: new ( ) ;
247
+ res. add_edge ( ( "b" , "a" , 1 ) ) ;
248
+ res. add_edge ( ( "c" , "a" , 1 ) ) ;
249
+ res. add_edge ( ( "b" , "c" , 1 ) ) ;
250
+ res. add_edge ( ( "c" , "d" , 1 ) ) ;
251
+ res. add_edge ( ( "d" , "b" , 1 ) ) ;
252
+ res
253
+ }
254
+ fn get_directed_graph_without_cycle ( ) -> DirectedGraph {
255
+ let mut res = DirectedGraph :: new ( ) ;
256
+ res. add_edge ( ( "b" , "a" , 1 ) ) ;
257
+ res. add_edge ( ( "c" , "a" , 1 ) ) ;
258
+ res. add_edge ( ( "b" , "c" , 1 ) ) ;
259
+ res. add_edge ( ( "c" , "d" , 1 ) ) ;
260
+ res. add_edge ( ( "b" , "d" , 1 ) ) ;
261
+ res
262
+ }
263
+ macro_rules! test_detect_cycle {
264
+ ( $( $name: ident: $test_case: expr, ) * ) => {
265
+ $(
266
+ #[ test]
267
+ fn $name( ) {
268
+ let ( graph, has_cycle) = $test_case;
269
+ println!( "detect_cycle_dfs: {}" , graph. detect_cycle_dfs( ) ) ;
270
+ println!( "detect_cycle_bfs: {}" , graph. detect_cycle_bfs( ) ) ;
271
+ assert_eq!( graph. detect_cycle_dfs( ) , has_cycle) ;
272
+ assert_eq!( graph. detect_cycle_bfs( ) , has_cycle) ;
273
+ }
274
+ ) *
275
+ } ;
276
+ }
277
+ test_detect_cycle ! {
278
+ undirected_empty: ( UndirectedGraph :: new( ) , false ) ,
279
+ directed_empty: ( DirectedGraph :: new( ) , false ) ,
280
+ undirected_single_node_with_loop: ( get_undirected_single_node_with_loop( ) , true ) ,
281
+ directed_single_node_with_loop: ( get_directed_single_node_with_loop( ) , true ) ,
282
+ undirected_two_nodes_connected: ( get_undirected_two_nodes_connected( ) , false ) ,
283
+ directed_two_nodes_connected: ( get_directed_two_nodes_connected( ) , true ) ,
284
+ directed_two_nodes: ( get_directed_two_nodes( ) , false ) ,
285
+ undirected_triangle: ( get_undirected_triangle( ) , true ) ,
286
+ undirected_triangle_with_tail: ( get_undirected_triangle_with_tail( ) , true ) ,
287
+ directed_triangle: ( get_directed_triangle( ) , true ) ,
288
+ directed_triangle_with_tail: ( get_directed_triangle_with_tail( ) , true ) ,
289
+ undirected_graph_with_cycle: ( get_undirected_graph_with_cycle( ) , true ) ,
290
+ undirected_graph_without_cycle: ( get_undirected_graph_without_cycle( ) , false ) ,
291
+ directed_graph_with_cycle: ( get_directed_graph_with_cycle( ) , true ) ,
292
+ directed_graph_without_cycle: ( get_directed_graph_without_cycle( ) , false ) ,
220
293
}
221
294
}
0 commit comments