@@ -77,77 +77,80 @@ public List<T> getResult() {
7777 * <p>
7878 * See https://en.wikipedia.org/wiki/Path-based_strong_component_algorithm
7979 */
80- public Set <Set <T >> findStronglyConnectedComponents (List <T > nodes ) {
81- Deque <T > s = new ArrayDeque <>();
82- Deque <T > p = new ArrayDeque <>();
83- AtomicInteger c = new AtomicInteger ();
84- AtomicInteger componentCount = new AtomicInteger ();
85- Object2IntLinkedOpenHashMap <T > preorderNumber = new Object2IntLinkedOpenHashMap <>();
86- Object2IntLinkedOpenHashMap <T > component = new Object2IntLinkedOpenHashMap <>();
87-
88- // This stack simulates the recursive calls
89- Deque <T > traversalStack = new ArrayDeque <>();
90- // This map holds iterators for the children of each node
91- Map <T , Iterator <T >> iterators = new HashMap <>();
92-
93- for (T startNode : nodes ) {
94- if (!preorderNumber .containsKey (startNode )) {
95- traversalStack .push (startNode );
96-
97- while (!traversalStack .isEmpty ()) {
98- T v = traversalStack .peek ();
99-
100- // Pre-order processing (first time visiting node v)
101- if (!preorderNumber .containsKey (v )) {
102- preorderNumber .put (v , c .getAndIncrement ());
103- s .push (v );
104- p .push (v );
105- iterators .put (v , getIncidentNodes (v ).iterator ());
106- }
80+ public List <List <T >> findStronglyConnectedComponents (List <T > nodes ) {
81+ Deque <T > s = new ArrayDeque <>(); // S stack
82+ Deque <T > p = new ArrayDeque <>(); // P stack
83+
84+ int preorderCounter = 0 ;
85+ int componentCounter = 0 ;
86+
87+ Object2IntLinkedOpenHashMap <T > preorder = new Object2IntLinkedOpenHashMap <>();
88+ preorder .defaultReturnValue (-1 );
89+ Object2IntLinkedOpenHashMap <T > compId = new Object2IntLinkedOpenHashMap <>();
90+ compId .defaultReturnValue (-1 );
91+
92+ Deque <T > frameStack = new ArrayDeque <>(); // simulated recursion
93+ Map <T , Iterator <T >> childIters = new HashMap <>(); // per-node child iterators
10794
108- boolean foundNewChild = false ;
109- Iterator <T > children = iterators .get (v );
110-
111- // Iterate over children to find the next one to visit
112- while (children .hasNext ()) {
113- T w = children .next ();
114- if (!preorderNumber .containsKey (w )) {
115- // Found an unvisited child, push to stack to simulate recursive call
116- traversalStack .push (w );
117- foundNewChild = true ;
118- break ;
119- } else if (!component .containsKey (w )) {
120- // Child w has been visited but is not yet in a component
121- while (!p .isEmpty () && preorderNumber .getOrDefault (p .peek (), -1 ) > preorderNumber .get (w )) {
122- p .pop ();
123- }
95+ List <List <T >> sccs = new ArrayList <>();
96+
97+ for (T start : nodes ) {
98+ if (preorder .getInt (start ) != -1 ) continue ;
99+
100+ frameStack .push (start );
101+ while (!frameStack .isEmpty ()) {
102+ T v = frameStack .peek ();
103+
104+ // First time at v
105+ if (preorder .getInt (v ) == -1 ) {
106+ preorder .put (v , preorderCounter ++);
107+ s .push (v );
108+ p .push (v );
109+ childIters .put (v , getIncidentNodes (v ).iterator ());
110+ }
111+
112+ boolean descended = false ;
113+ Iterator <T > it = childIters .get (v );
114+
115+ while (it .hasNext ()) {
116+ T w = it .next ();
117+ int preW = preorder .getInt (w );
118+ if (preW == -1 ) {
119+ frameStack .push (w );
120+ descended = true ;
121+ break ;
122+ } else if (compId .getInt (w ) == -1 ) {
123+ // w discovered but not assigned; shrink P
124+ while (!p .isEmpty () && preorder .getInt (p .peek ()) > preW ) {
125+ p .pop ();
124126 }
125127 }
128+ }
126129
127- if (foundNewChild ) {
128- // Continue the loop to process the new child on top of the stack
129- continue ;
130- }
130+ if (descended ) continue ;
131131
132- // Post-order processing (all children of v have been visited)
133- traversalStack .pop (); // Finished with v, pop it
134- iterators .remove (v ); // Clean up iterator
135-
136- if (!p .isEmpty () && p .peek () == v ) {
137- Integer newComponent = componentCount .incrementAndGet ();
138- while (true ) {
139- T popped = s .pop ();
140- component .put (popped , newComponent );
141- if (popped == v ) {
142- break ;
143- }
144- }
145- p .pop ();
132+ // Post-order for v
133+ frameStack .pop ();
134+ childIters .remove (v );
135+
136+ if (!p .isEmpty () && p .peek () == v ) {
137+ int newCid = componentCounter ++;
138+ ArrayList <T > cur = new ArrayList <>();
139+ while (true ) {
140+ T x = s .pop ();
141+ compId .put (x , newCid );
142+ cur .add (x );
143+ if (x == v ) break ;
146144 }
145+ p .pop ();
146+
147+ // Gabow emits SCCs in reverse-topological order
148+ sccs .add (cur );
147149 }
148150 }
149151 }
150- return ImmutableSet .copyOf (Utils .inverseMapToSet (component ).values ());
152+
153+ return sccs ;
151154 }
152155
153156 public String generateDotFile (List <T > nodes ) {
0 commit comments