-
Notifications
You must be signed in to change notification settings - Fork 31.1k
Expand file tree
/
Copy pathdetectUndirectedCycle.js
More file actions
67 lines (57 loc) · 2.5 KB
/
detectUndirectedCycle.js
File metadata and controls
67 lines (57 loc) · 2.5 KB
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
import depthFirstSearch from '../depth-first-search/depthFirstSearch';
/**
* Detect cycle in undirected graph using Depth First Search.
*
* @param {Graph} graph
* @return {Object|null} - cycle object or null if no cycle detected.
* The cycle object has vertex keys as properties and parent vertices as values,
* representing the mapping of each vertex in the cycle to its predecessor.
*/
export default function detectUndirectedCycle(graph) {
let cycle = null;
// List of vertices that we have visited.
const visitedVertices = {};
// List of parents vertices for every visited vertex.
const parents = {};
// Callbacks for DFS traversing.
const callbacks = {
allowTraversal: ({ currentVertex, nextVertex }) => {
// Don't allow further traversal in case if cycle has been detected.
// This provides an early exit once a cycle is found, avoiding
// unnecessary traversal of the remaining graph.
if (cycle) {
return false;
}
// Don't allow traversal from child back to its parent.
const currentVertexParent = parents[currentVertex.getKey()];
const currentVertexParentKey = currentVertexParent ? currentVertexParent.getKey() : null;
return currentVertexParentKey !== nextVertex.getKey();
},
enterVertex: ({ currentVertex, previousVertex }) => {
if (visitedVertices[currentVertex.getKey()]) {
// Compile cycle path based on parents of previous vertices.
// The cycle is represented as an object where each key is a vertex
// in the cycle and its value is the previous vertex in the path.
cycle = {};
let currentCycleVertex = currentVertex;
let previousCycleVertex = previousVertex;
while (previousCycleVertex.getKey() !== currentVertex.getKey()) {
cycle[currentCycleVertex.getKey()] = previousCycleVertex;
currentCycleVertex = previousCycleVertex;
previousCycleVertex = parents[previousCycleVertex.getKey()];
}
cycle[currentCycleVertex.getKey()] = previousCycleVertex;
} else {
// Add next vertex to visited set.
visitedVertices[currentVertex.getKey()] = currentVertex;
parents[currentVertex.getKey()] = previousVertex;
}
},
};
// Start DFS traversing from the first vertex.
// Note: For disconnected graphs, this will only detect cycles in the
// component containing the first vertex.
const startVertex = graph.getAllVertices()[0];
depthFirstSearch(graph, startVertex, callbacks);
return cycle;
}