Tarjan’s strongly connected components algorithm

unsorted — cgrand, 18 March 2013 @ 19 h 48 min

I dislike algorithms that are full of indices and mutations. Not because they are bad but because I always have the feeling that the core idea is buried. As such, Tarjan’s SCC algorithm irked me.

So I took the traditional algorithm, implemented it in Clojure with explicit environment passing, then I replaced indices by explicit stacks (thanks to persistence!) and after some tweaks, I realized that I’ve gone full circle and could switch to stacks lengths instead of stacks themselves and get rid of the loop. However the whole process made the code cleaner to my eye. You can look at the whole history.

Here is the resulting code:

(defn tarjan 
  "Returns the strongly connected components of a graph specified by its nodes
   and a successor function succs from node to nodes.
   The used algorithm is Tarjan's one."
  [nodes succs]
  (letfn [(sc [env node]
            ; env is a map from nodes to stack length or nil,
            ; nil means the node is known to belong to another SCC
            ; there are two special keys: ::stack for the current stack 
            ; and ::sccs for the current set of SCCs
            (if (contains? env node)
              env
              (let [stack (::stack env)
                    n (count stack)
                    env (assoc env node n ::stack (conj stack node))
                    env (reduce (fn [env succ]
                                  (let [env (sc env succ)]
                                    (assoc env node (min (or (env succ) n) (env node)))))
                          env (succs node))]
                (if (= n (env node)) ; no link below us in the stack, call it a SCC
                  (let [nodes (::stack env)
                        scc (set (take (- (count nodes) n) nodes))
                        ; clear all stack lengths for these nodes since this SCC is done
                        env (reduce #(assoc %1 %2 nil) env scc)]
                    (assoc env ::stack stack ::sccs (conj (::sccs env) scc)))
                  env))))]
    (::sccs (reduce sc {::stack () ::sccs #{}} nodes))))

As always, if you need some short-term help with Clojure (code review, consulting, training etc.), contact me!

5 Comments »

  1. Hi,

    Interesting article as always!

    The link to “Tarjan’s SCC algorithm” is broken. I guess you meant to use http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm .

    - Max

    Comment by mpenet — 18 March 2013 @ 21 h 54 min
  2. @Max link fixed, thanks!

    Comment by cgrand — 19 March 2013 @ 12 h 02 min
  3. Hello to every single one, it’s in fact a fastidious for me to visit this web page,
    it includes useful Information.

    Comment by Derek — 23 August 2014 @ 18 h 03 min
  4. Very good info. Lucky me I found your blog by chance (stumbleupon).
    I have book marked it for later!

    Comment by hearthstone cheat engine gold — 29 August 2014 @ 7 h 01 min
  5. I really like it when folks get together and shate thoughts.

    Grdat site, keep it up!

    Comment by Annett — 15 September 2014 @ 4 h 06 min

RSS feed for comments on this post. TrackBack URI

Leave a comment

(c) 2014 Clojure and me | powered by WordPress with Barecity