-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathconnected-components.lisp
77 lines (69 loc) · 3.23 KB
/
connected-components.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
(in-package #:graph-algorithms)
(defun connected-components (vertices neighbors-fn visitor-fn)
"VERTICES is the list of vertices of the graph. NEIGHBORS-FN should
return a list of immediate neighbor vertices of a given vertex.
VISITOR-FN is called once for each representative vertex of found
components."
(let ((discovered (make-hash-table)))
(dolist (id vertices)
(unless (gethash id discovered)
(setf (gethash id discovered) t)
(funcall visitor-fn id)
(breadth-first-search id neighbors-fn
(lambda (n)
(setf (gethash n discovered) t)))))))
(defun strongly-connected-components (vertices neighbors-fn visitor-fn)
"Tarjan's algorithm. Performs a single pass of depth first
search. It maintains a stack of vertices that have been explored by
the search but not yet assigned to a component, and calculates low
numbers of each vertex (an index number of the highest ancestor
reachable in one step from a descendant of the vertex) which it uses
to determine when a set of vertices should be popped off the stack
into a new component.
VERTICES is the list of vertices of the graph. NEIGHBORS-FN should
return a list of immediate neighbor vertices of a given vertex.
VISITOR-FN is called once for each SCC found.
This implementation is a naive translation of the pseudocode in
Wikipedia, not really optimized for any particular workflow."
(let ((index 0)
(stack nil)
(on-stack (make-hash-table))
(indices (make-hash-table))
(low-links (make-hash-table)))
(labels ((set-low-link (v n) (setf (gethash v low-links) n))
(get-low-link (v) (gethash v low-links))
(set-index (v n) (setf (gethash v indices) n))
(get-index (v) (gethash v indices))
(set-on-stack (v n) (setf (gethash v on-stack) n))
(get-on-stack (v) (gethash v on-stack))
(strong-connect (v)
(set-index v index)
(set-low-link v index)
(incf index)
(push v stack)
(set-on-stack v t)
;; consider sucessors of v
(dolist (w (funcall neighbors-fn v))
(if (not (get-index w))
;; sucessor w has not yet been visited; recurse on it
(progn
(strong-connect w)
(set-low-link v (min (get-low-link v) (get-low-link w))))
(if (get-on-stack w)
;; sucessor w is in stack and hence in the current
;; SCC
(set-low-link v (min (get-low-link v) (get-index w))))))
;; if v is a root node, pop the stack and generate a SCC
(when (= (get-low-link v) (get-index v))
(let ((w nil)
(connected-component nil))
(loop until (eql v w)
do
(setf w (pop stack))
(set-on-stack w nil)
(push w connected-component))
;; emit the SCC
(funcall visitor-fn connected-component)))))
(dolist (v vertices)
(when (not (get-index v))
(strong-connect v))))))