@@ -4,56 +4,57 @@ import depthFirstSearch from '../depth-first-search/depthFirstSearch';
4
4
* Detect cycle in undirected graph using Depth First Search.
5
5
*
6
6
* @param {Graph } graph
7
+ * @returns {Vertex[] | null } ordered array of vertices forming the cycle (first === last), or null
7
8
*/
8
9
export default function detectUndirectedCycle ( graph ) {
9
- let cycle = null ;
10
+ let cycle = null ; // will hold ordered array once found
10
11
11
- // List of vertices that we have visited.
12
- const visitedVertices = { } ;
12
+ const visitedVertices = { } ; // visited vertices
13
+ const parents = { } ; // parent for every visited vertex
13
14
14
- // List of parents vertices for every visited vertex.
15
- const parents = { } ;
16
-
17
- // Callbacks for DFS traversing.
18
15
const callbacks = {
19
16
allowTraversal : ( { currentVertex, nextVertex } ) => {
20
- // Don't allow further traversal in case if cycle has been detected.
21
- if ( cycle ) {
22
- return false ;
23
- }
17
+ if ( cycle ) return false ; // stop traversal once cycle is found
24
18
25
- // Don't allow traversal from child back to its parent.
26
19
const currentVertexParent = parents [ currentVertex . getKey ( ) ] ;
27
- const currentVertexParentKey = currentVertexParent ? currentVertexParent . getKey ( ) : null ;
20
+ const currentVertexParentKey = currentVertexParent
21
+ ? currentVertexParent . getKey ( )
22
+ : null ;
28
23
29
24
return currentVertexParentKey !== nextVertex . getKey ( ) ;
30
25
} ,
26
+
31
27
enterVertex : ( { currentVertex, previousVertex } ) => {
32
28
if ( visitedVertices [ currentVertex . getKey ( ) ] ) {
33
- // Compile cycle path based on parents of previous vertices.
34
- cycle = { } ;
35
-
36
- let currentCycleVertex = currentVertex ;
37
- let previousCycleVertex = previousVertex ;
38
-
39
- while ( previousCycleVertex . getKey ( ) !== currentVertex . getKey ( ) ) {
40
- cycle [ currentCycleVertex . getKey ( ) ] = previousCycleVertex ;
41
- currentCycleVertex = previousCycleVertex ;
42
- previousCycleVertex = parents [ previousCycleVertex . getKey ( ) ] ;
29
+ // Build ordered cycle array
30
+ const startKey = currentVertex . getKey ( ) ;
31
+ const cycleArr = [ currentVertex ] ;
32
+
33
+ let walker = previousVertex ;
34
+ while ( walker && walker . getKey ( ) !== startKey ) {
35
+ cycleArr . push ( walker ) ;
36
+ walker = parents [ walker . getKey ( ) ] ;
43
37
}
44
38
45
- cycle [ currentCycleVertex . getKey ( ) ] = previousCycleVertex ;
39
+ cycleArr . push ( currentVertex ) ; // close the cycle
40
+ cycle = cycleArr ;
46
41
} else {
47
- // Add next vertex to visited set.
48
42
visitedVertices [ currentVertex . getKey ( ) ] = currentVertex ;
49
43
parents [ currentVertex . getKey ( ) ] = previousVertex ;
50
44
}
51
45
} ,
52
46
} ;
53
47
54
- // Start DFS traversing.
55
- const startVertex = graph . getAllVertices ( ) [ 0 ] ;
56
- depthFirstSearch ( graph , startVertex , callbacks ) ;
48
+ const allVertices = graph . getAllVertices ( ) ;
49
+ for ( let i = 0 ; i < allVertices . length ; i += 1 ) {
50
+ const startVertex = allVertices [ i ] ;
51
+
52
+ if ( ! visitedVertices [ startVertex . getKey ( ) ] ) {
53
+ depthFirstSearch ( graph , startVertex , callbacks ) ;
54
+
55
+ if ( cycle ) break ; // early exit once cycle is found
56
+ }
57
+ }
57
58
58
59
return cycle ;
59
60
}
0 commit comments