-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathdisjoint-set.lisp
59 lines (53 loc) · 2.08 KB
/
disjoint-set.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
(defpackage :cp/disjoint-set
(:use :cl)
(:export #:disjoint-set #:make-disjoint-set #:ds-data
#:ds-root #:ds-unite! #:ds-connected-p #:ds-size #:ds-clear)
(:documentation "Provides disjoint set implementation with union by size and
path compression."))
(in-package :cp/disjoint-set)
(defstruct (disjoint-set
(:constructor make-disjoint-set
(size &aux (data (make-array size :element-type 'fixnum :initial-element -1))))
(:conc-name ds-)
(:predicate nil)
(:copier nil))
(data nil :type (simple-array fixnum (*))))
(declaim (inline ds-root))
(defun ds-root (disjoint-set x)
"Returns the root of X."
(declare ((mod #.array-dimension-limit) x))
(let ((data (ds-data disjoint-set)))
(labels ((recur (x)
(if (< (aref data x) 0)
x
(setf (aref data x)
(recur (aref data x))))))
(recur x))))
(declaim (inline ds-unite!))
(defun ds-unite! (disjoint-set x1 x2)
"Destructively unites X1 and X2 and returns true iff X1 and X2 become
connected for the first time."
(let ((root1 (ds-root disjoint-set x1))
(root2 (ds-root disjoint-set x2)))
(unless (= root1 root2)
(let ((data (ds-data disjoint-set)))
;; NOTE: If you want X1 to always be root, just delete this form. (Time
;; complexity becomes worse, however.)
(when (> (aref data root1) (aref data root2))
(rotatef root1 root2))
(incf (aref data root1) (aref data root2))
(setf (aref data root2) root1)))))
(declaim (inline ds-connected-p))
(defun ds-connected-p (disjoint-set x1 x2)
"Returns true iff X1 and X2 have the same root."
(= (ds-root disjoint-set x1) (ds-root disjoint-set x2)))
(declaim (inline ds-size))
(defun ds-size (disjoint-set x)
"Returns the size of the connected component to which X belongs."
(- (aref (ds-data disjoint-set)
(ds-root disjoint-set x))))
(declaim (inline ds-clear))
(defun ds-clear (disjoint-set)
"Deletes all connections."
(fill (ds-data disjoint-set) -1)
disjoint-set)