@@ -26,6 +26,7 @@ module Data.Graph
26
26
, isAdjacent
27
27
, areConnected
28
28
, shortestPath
29
+ , stronglyConnectedComponents
29
30
, allPaths
30
31
) where
31
32
@@ -42,11 +43,12 @@ import Data.List as L
42
43
import Data.List as List
43
44
import Data.Map (Map )
44
45
import Data.Map as M
45
- import Data.Maybe (Maybe (..), isJust , maybe )
46
+ import Data.Maybe (Maybe (..), fromJust , isJust , maybe )
46
47
import Data.Set (Set )
47
48
import Data.Set as S
48
49
import Data.Set as Set
49
50
import Data.Tuple (Tuple (..), fst , snd , uncurry )
51
+ import Partial.Unsafe (unsafePartial )
50
52
51
53
-- | A graph with vertices of type `v`.
52
54
-- |
@@ -271,3 +273,61 @@ topologicalSort (Graph g) =
271
273
initialState = { unvisited: g
272
274
, result: Nil
273
275
}
276
+
277
+ -- | Tarjan's algorithm for Strongly Connected Components (SCCs).
278
+ -- |
279
+ -- | Defines a `Map` where each node is mapped to its equivalence class
280
+ -- | representative once the graph is partitioned into SCCs.
281
+ -- |
282
+ -- | Running time: O(|E| log |V|)
283
+ stronglyConnectedComponents :: forall k v . Show k => Ord k => Graph k v -> Map k k
284
+ stronglyConnectedComponents (Graph g) =
285
+ Foldable .foldl
286
+ ( \scc next ->
287
+ if isJust (M .lookup next scc) then
288
+ -- have already found the SCC representative of `next`
289
+ scc
290
+ else
291
+ dfs next M .empty scc
292
+ )
293
+ M .empty
294
+ $ M .keys g
295
+ where
296
+ dfs :: k -> Map k Int -> Map k k -> Map k k
297
+ -- perform a depth-first search in the graph starting from `n`
298
+ -- `depth` tracks the depth at which each node is visited
299
+ -- `scc` is the accumulating map from each node to its representative in its
300
+ -- SCC
301
+ -- - when `scc` is defined on all vertices of the graph, the procedure is
302
+ -- complete
303
+ dfs n depth scc = case ( Foldable .foldl
304
+ ( \(Tuple sccAcc shallowest) c -> case M .lookup c depth of
305
+ -- fold over the children `c` of `n`; the accumulator is a pair
306
+ -- consisting of
307
+ -- - `sccAcc`: the accumulating SCC map
308
+ -- - `shallowest`: the shallowest node we have seen so far in
309
+ -- this branch of the DFS (this will become the
310
+ -- SCC representative)
311
+ Just _ -> Tuple sccAcc $ shallower shallowest c
312
+ Nothing ->
313
+ let
314
+ sccWithChildData = dfs c (M .insert n (M .size depth) depth) sccAcc
315
+
316
+ shallowestForChild = unsafePartial $ fromJust $ M .lookup c sccWithChildData
317
+ -- not actually partial; `c` is guaranteed to be in the map
318
+ in
319
+ Tuple sccWithChildData $ shallower shallowest shallowestForChild
320
+ )
321
+ (Tuple scc n)
322
+ $ children n (Graph g)
323
+ ) of
324
+ Tuple newState shallow -> M .insert n shallow newState -- `shallow` becomes the representative
325
+ -- of the SCC containing `n`
326
+ where
327
+ shallower :: k -> k -> k
328
+ -- determine whether `x` or `y` was visited first in the DFS, as determined
329
+ -- by `depth`
330
+ shallower x y = case (Tuple (M .lookup x depth) (M .lookup y depth)) of
331
+ Tuple _ Nothing -> x
332
+ Tuple Nothing _ -> y
333
+ Tuple (Just p) (Just q) -> if q < p then y else x
0 commit comments