Add detect cycle.

This commit is contained in:
Oleksii Trekhleb
2018-05-06 17:57:45 +03:00
parent 84ed7e409d
commit eec2df994c
5 changed files with 125 additions and 26 deletions

View File

@@ -1,32 +1,59 @@
import DisjointSet from '../../../data-structures/disjoint-set/DisjointSet';
import depthFirstSearch from '../depth-first-search/depthFirstSearch';
/**
* Detect cycle in undirected graph using disjoint sets.
* Detect cycle in undirected graph using Depth First Search.
*
* @param {Graph} graph
*/
export default function detectUndirectedCycle(graph) {
// Create initial singleton disjoint sets for each graph vertex.
/** @param {GraphVertex} graphVertex */
const keyExtractor = graphVertex => graphVertex.getKey();
const disjointSet = new DisjointSet(keyExtractor);
graph.getAllVertices().forEach(graphVertex => disjointSet.makeSet(graphVertex));
let cycle = null;
// Go trough all graph edges one by one and check if edge vertices are from the
// different sets. In this case joint those sets together. Do this until you find
// an edge where to edge vertices are already in one set. This means that current
// edge will create a cycle.
let cycleFound = false;
/** @param {GraphEdge} graphEdge */
graph.getAllEdges().forEach((graphEdge) => {
if (disjointSet.inSameSet(graphEdge.startVertex, graphEdge.endVertex)) {
// Cycle found.
cycleFound = true;
} else {
disjointSet.union(graphEdge.startVertex, graphEdge.endVertex);
}
});
// List of vertices that we have visited.
const visitedVertices = {};
return cycleFound;
// 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.
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.
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.
const startVertex = graph.getAllVertices()[0];
depthFirstSearch(graph, startVertex, callbacks);
return cycle;
}