Skip to content

Commit 3174c61

Browse files
committed
feat: add condensation function using Kosaraju's algorithm for SCCs
1 parent 08d8c6b commit 3174c61

File tree

1 file changed

+131
-0
lines changed

1 file changed

+131
-0
lines changed

Graphs/Condensation.js

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/**
2+
* Condensation via Kosaraju's algorithm
3+
*
4+
* @see https://cp-algorithms.com/graph/strongly-connected-components.html
5+
* This file computes the strongly connected components (SCCs) and the
6+
* condensation (component DAG) for a directed graph given as an edge list.
7+
* It uses the project's existing Kosaraju implementation (Kosaraju.js).
8+
*/
9+
10+
import { kosaraju } from './Kosaraju.js'
11+
12+
/**
13+
* Compute SCCs and the condensation graph (component DAG) for a directed graph.
14+
* @param {Array<Array<number>>} graph edges as [u, v]
15+
* @returns {{sccs: Array<Array<number>>, condensation: Array<Array<number>>}}
16+
*/
17+
18+
function condensation(graph) {
19+
// Using Kosaraju implementation to compute SCCs
20+
const sccs = kosaraju(graph)
21+
22+
// Build adjacency map to include isolated nodes
23+
const g = {}
24+
const addNode = (n) => {
25+
if (!(n in g)) g[n] = new Set()
26+
}
27+
for (const [u, v] of graph) {
28+
addNode(u)
29+
addNode(v)
30+
g[u].add(v)
31+
}
32+
33+
// Map node -> component index (string keys)
34+
const compIndex = {}
35+
for (let i = 0; i < sccs.length; i++) {
36+
for (const node of sccs[i]) compIndex[String(node)] = i
37+
}
38+
39+
// Build condensation adjacency list (component graph)
40+
const condensationSets = Array.from({ length: sccs.length }, () => new Set())
41+
for (const u of Object.keys(g)) {
42+
const ci = compIndex[u]
43+
if (ci === undefined) continue
44+
for (const v of g[u]) {
45+
const cj = compIndex[String(v)]
46+
if (cj === undefined) continue
47+
if (ci !== cj) condensationSets[ci].add(cj)
48+
}
49+
}
50+
51+
const condensation = condensationSets.map((s) => Array.from(s))
52+
53+
return { sccs, condensation, compIndex }
54+
}
55+
56+
export { condensation }
57+
58+
/**
59+
* Time and Space Complexity
60+
*
61+
* Kosaraju's algorithm (finding SCCs) and condensation graph construction:
62+
*
63+
* - Time complexity: O(V + E)
64+
* - Building the adjacency list from the input edge list: O(E)
65+
* - Running Kosaraju's two DFS passes over all vertices and edges: O(V + E)
66+
* - Building the component index and condensation edges: O(V + E)
67+
* Overall: O(V + E), where V is the number of vertices and E is the number of edges.
68+
*
69+
* - Space complexity: O(V + E)
70+
* - Adjacency lists (graph): O(V + E)
71+
* - Kosaraju bookkeeping (stacks, visited sets, topo order, sccs, compIndex): O(V)
72+
* - Condensation adjacency structures: O(C + E') where C <= V and E' <= E, so
73+
* bounded by O(V + E).
74+
* Overall: O(V + E).
75+
*
76+
* Notes:
77+
* - Recursion may use O(V) call-stack space in the worst case during DFS.
78+
* - Constants depend on the data-structures used (Sets/Arrays), but the
79+
* asymptotic bounds above hold for this implementation.
80+
*/
81+
82+
/**
83+
* Visual diagram and usage
84+
*
85+
* Consider the directed edge list:
86+
*
87+
* edges = [
88+
* [1,2], [2,3], [3,1], // cycle among 1,2,3
89+
* [2,4], // edge from the first cycle to node 4
90+
* [4,5], [5,6], [6,4] // cycle among 4,5,6
91+
* ]
92+
*
93+
* Original graph (conceptual):
94+
*
95+
* 1 --> 2 --> 4 --> 5
96+
* ^ | |
97+
* | v v
98+
* 3 <-- - 6
99+
*
100+
* Strongly connected components (SCCs) in this graph:
101+
*
102+
* SCC A: [1, 2, 3] // a cycle 1->2->3->1
103+
* SCC B: [4, 5, 6] // a cycle 4->5->6->4
104+
*
105+
* Condensation graph (component DAG):
106+
*
107+
* [A] ---> [B]
108+
*
109+
* Where each node in the condensation is a whole SCC from the original graph.
110+
*
111+
* Example return values from `condensation(edges)`:
112+
*
113+
* {
114+
* sccs: [[1,2,3], [4,5,6]],
115+
* condensation: [[1], []], // component 0 has an edge to component 1
116+
* compIndex: { '1': 0, '2': 0, '3': 0, '4': 1, '5': 1, '6': 1 }
117+
* }
118+
*
119+
* Usage:
120+
*
121+
* const edges = [[1,2],[2,3],[3,1],[2,4],[4,5],[5,6],[6,4]]
122+
* const { sccs, condensation, compIndex } = condensation(edges)
123+
*
124+
* Interpretation summary:
125+
* - `sccs` is an array of components; each component is an array of original nodes.
126+
* - `condensation` is the adjacency list of the component DAG; indices refer to `sccs`.
127+
* - `compIndex` maps an original node (string key) to its component index.
128+
*
129+
* This ASCII diagram and mapping should make it easy to visualize how the
130+
* condensation function groups cycles and produces a DAG of components.
131+
*/

0 commit comments

Comments
 (0)