From 702cc6b6f2f648ae54d1d0758877d2f3fb4f9556 Mon Sep 17 00:00:00 2001 From: karthi1953 Date: Wed, 30 Jul 2025 10:16:50 +0530 Subject: [PATCH] Cycle issue fixed --- .../detectUndirectedCycleUsingDisjointSet.js | 73 ++++++++++++++----- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/src/algorithms/graph/detect-cycle/detectUndirectedCycleUsingDisjointSet.js b/src/algorithms/graph/detect-cycle/detectUndirectedCycleUsingDisjointSet.js index 7dec9c2890..893ae79dbf 100644 --- a/src/algorithms/graph/detect-cycle/detectUndirectedCycleUsingDisjointSet.js +++ b/src/algorithms/graph/detect-cycle/detectUndirectedCycleUsingDisjointSet.js @@ -1,31 +1,64 @@ import DisjointSet from '../../../data-structures/disjoint-set/DisjointSet'; /** - * Detect cycle in undirected graph using disjoint sets. - * + * Detect and return the actual cycle path in an undirected graph using disjoint sets. * @param {Graph} graph + * @returns {Array|null} - Returns an array of vertex keys forming the cycle, or null if no cycle found. */ export default function detectUndirectedCycleUsingDisjointSet(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)); - - // 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); - } + const parentMap = new Map(); + + graph.getAllVertices().forEach((vertex) => { + disjointSet.makeSet(vertex); + parentMap.set(vertex.getKey(), null); // Initialize with no parent }); - return cycleFound; + for (const edge of graph.getAllEdges()) { + const startKey = edge.startVertex.getKey(); + const endKey = edge.endVertex.getKey(); + + if (disjointSet.inSameSet(edge.startVertex, edge.endVertex)) { + // Cycle detected: reconstruct cycle path + return constructCyclePath(startKey, endKey, parentMap); + } + + // Save parent info (arbitrarily choosing one as child) + parentMap.set(endKey, startKey); + disjointSet.union(edge.startVertex, edge.endVertex); + } + + return null; +} + +/** + * Construct an ordered cycle path using parent map. + * @param {string} startKey + * @param {string} endKey + * @param {Map} parentMap + * @returns {string[]} Ordered array of vertex keys forming a cycle + */ +function constructCyclePath(startKey, endKey, parentMap) { + const pathToRoot = (key) => { + const path = []; + while (key !== null) { + path.push(key); + key = parentMap.get(key); + } + return path; + }; + + const pathStart = pathToRoot(startKey); + const pathEnd = pathToRoot(endKey); + + // Find the last common ancestor + const setStart = new Set(pathStart); + const commonAncestor = pathEnd.find((key) => setStart.has(key)); + + // Slice paths up to the common ancestor + const cycleStart = pathStart.slice(0, pathStart.indexOf(commonAncestor) + 1); + const cycleEnd = pathEnd.slice(0, pathEnd.indexOf(commonAncestor)).reverse(); + + return [...cycleStart, ...cycleEnd, commonAncestor]; }