scsh-0.6/scheme/big/search-tree.scm

442 lines
14 KiB
Scheme
Raw Normal View History

2003-05-01 06:21:33 -04:00
; Copyright (c) 1993-1999 by Richard Kelsey and Jonathan Rees. See file COPYING.
; Red-Black binary search trees as described in Introduction to Algorithms
; by Cormen, Leiserson, and Rivest. Look there if you want to understand
; the algorithm.
;
; These are like tables in that the value of a key defaults to #f.
;
; (make-search-tree key-= key-<) -> tree
;
; (search-tree? value) -> boolean
;
; (search-tree-ref tree key) -> value
;
; (search-tree-set! tree key value)
;
; (search-tree-modify! tree key proc)
; == (search-tree-set! tree key (proc (search-tree-ref tree key)))
;
; (search-tree-max tree) -> key + value
; (pop-search-tree-max! tree) -> key + value (removes entry)
;
; (search-tree-min tree) -> key + value
; (pop-search-tree-min! tree) -> key + value (removes entry)
;
; (walk-search-tree proc tree)
; applies PROC in order to all key + value pairs with a non-#f value
(define-record-type tree :tree
(make-tree lookup nil root)
search-tree?
(lookup tree-lookup)
(nil tree-nil) ; node marker for missing leaf nodes
(root tree-root set-tree-root!))
(define (make-search-tree = <)
(let ((nil (make-node #f #f #f)))
(set-node-red?! nil #f)
(make-tree (make-lookup = <) nil #f)))
(define-record-type node :node
(really-make-node key value parent red? left right)
node?
(key node-key set-node-key!)
(value node-value set-node-value!)
(parent node-parent set-node-parent!) ; #f in the root node
(red? node-red? set-node-red?!) ; for balancing the tree
(left node-left set-node-left!) ; left and
(right node-right set-node-right!)) ; right subtrees
(define (make-node key value parent)
(really-make-node key value parent #t #f #f))
(define-record-discloser :node
(lambda (node)
(list 'node (node-key node))))
; Lookup up KEY and return its value.
(define (search-tree-ref tree key)
(call-with-values
(lambda ()
((tree-lookup tree) tree key))
(lambda (node parent left?)
(if node
(node-value node)
#f))))
; Adding and modifying entries.
(define (search-tree-set! tree key value)
(search-tree-modify! tree key (lambda (ignore) value)))
(define (search-tree-modify! tree key proc)
(call-with-values
(lambda ()
((tree-lookup tree) tree key))
(lambda (node parent left?)
(let ((new-value (proc (if node (node-value node) #f))))
(cond ((and node new-value)
(set-node-value! node new-value))
(new-value
(really-insert! tree parent left? (make-node key new-value parent)))
(node
(really-delete! tree node)))))))
; Min and max entries.
(define (search-tree-max tree)
(real-search-tree-max tree #f))
(define (pop-search-tree-max! tree)
(real-search-tree-max tree #t))
(define (real-search-tree-max tree delete?)
(let ((node (tree-root tree)))
(if node
(let loop ((node node))
(cond ((node-right node)
=> loop)
(else
(if delete?
(really-delete! tree node))
(values (node-key node) (node-value node)))))
(values #f #f))))
(define (search-tree-min tree)
(real-search-tree-min tree #f))
(define (pop-search-tree-min! tree)
(real-search-tree-min tree #t))
(define (real-search-tree-min tree delete?)
(let ((node (tree-root tree)))
(if node
(let loop ((node node))
(cond ((node-left node)
=> loop)
(else
(if delete?
(really-delete! tree node))
(values (node-key node) (node-value node)))))
(values #f #f))))
(define (walk-search-tree proc tree)
(let recur ((node (tree-root tree)))
(cond (node
(recur (node-left node))
(proc (node-key node) (node-value node))
(recur (node-right node))))))
; Lookup up an entry. Easy.
;
; Hack of checking common case reduced lookup time in a 1000 element search
; tree by a third.
(define (make-lookup tree-= tree-<)
(if (and (eq? tree-= =)
(eq? tree-< <))
default-lookup
(lambda (tree key)
(let loop ((node (tree-root tree))
(parent #f)
(left? #f))
(cond ((not node)
(values #f parent left?))
((tree-= (node-key node) key)
(values node #f #f))
((tree-< key (node-key node))
(loop (node-left node) node #t))
(else
(loop (node-right node) node #f)))))))
(define (default-lookup tree key)
(let loop ((node (tree-root tree))
(parent #f)
(left? #f))
(cond ((not node)
(values #f parent left?))
((= (node-key node) key)
(values node #f #f))
((< key (node-key node))
(loop (node-left node) node #t))
(else
(loop (node-right node) node #f)))))
;----------------------------------------------------------------
; Little utilities.
; Parameterized node access
(define (node-child node left?)
(if left?
(node-left node)
(node-right node)))
(define (set-node-child! node left? child)
(if left?
(set-node-left! node child)
(set-node-right! node child)))
; Empty leaf slots are considered black.
(define (node-black? node)
(not (and node (node-red? node))))
; The next node (used in REALLY-DELETE!)
(define (successor node)
(cond ((node-right node)
=> (lambda (node)
(let loop ((node node))
(cond ((node-left node)
=> loop)
(else node)))))
(else
(let loop ((node node) (parent (node-parent node)))
(if (and parent
(eq? node (node-right parent)))
(loop parent (node-parent parent))
parent)))))
;----------------------------------------------------------------
; Add NODE as the LEFT? child of PARENT and balance the tree.
(define (really-insert! tree parent left? node)
(if (not parent)
(set-tree-root! tree node)
(set-node-child! parent left? node))
(fixup-insertion! node tree))
; Balance the tree after NODE has been inserted.
(define (fixup-insertion! node tree)
(let loop ((node node))
(let ((parent (node-parent node)))
(if (and parent (node-red? parent))
(let* ((grand (node-parent parent))
(left? (eq? parent (node-left grand)))
(y (node-child grand (not left?))))
(cond ((node-black? y)
(let* ((node (cond ((eq? node (node-child parent (not left?)))
(rotate! parent left? tree)
parent)
(else node)))
(parent (node-parent node))
(grand (node-parent parent)))
(set-node-red?! parent #f)
(set-node-red?! grand #t)
(rotate! grand (not left?) tree)
(loop node)))
(else
(set-node-red?! parent #f)
(set-node-red?! y #f)
(set-node-red?! grand #t)
(loop grand)))))))
(set-node-red?! (tree-root tree) #f))
; A B
; / \ =(rotate! A #f tree)=> / \
; B k i A
; / \ <=(rotate! B #t tree)= / \
; i j j k
(define (rotate! node left? tree)
(let* ((y (node-child node (not left?)))
(y-left (node-child y left?))
(parent (node-parent node)))
(set-node-child! node (not left?) y-left)
(if y-left
(set-node-parent! y-left node))
(replace! parent y node tree)
(set-node-child! y left? node)
(set-node-parent! node y)))
; Replace CHILD (of PARENT) with NEW-CHILD
(define (replace! parent new-child child tree)
(set-node-parent! new-child parent)
(cond ((eq? child (tree-root tree))
(set-tree-root! tree new-child))
((eq? child (node-left parent))
(set-node-left! parent new-child))
(else
(set-node-right! parent new-child))))
; Remove NODE from tree.
(define (really-delete! tree node)
(let* ((y (cond ((or (not (node-left node))
(not (node-right node)))
node)
(else
(let ((y (successor node)))
(set-node-key! node (node-key y))
(set-node-value! node (node-value y))
y))))
(x (or (node-left y)
(node-right y)
(let ((x (tree-nil tree)))
(set-node-right! y x)
x)))
(parent (node-parent y)))
(replace! parent x y tree)
(if (not (node-red? y))
(fixup-delete! x tree))
(let ((nil (tree-nil tree)))
(cond ((node-parent nil)
=> (lambda (p)
(if (eq? (node-right p) nil)
(set-node-right! p #f)
(set-node-left! p #f))
(set-node-parent! (tree-nil tree) #f)))
((eq? nil (tree-root tree))
(set-tree-root! tree #f))))))
(define (fixup-delete! x tree)
(let loop ((x x))
(if (or (eq? x (tree-root tree))
(node-red? x))
(set-node-red?! x #f)
(let* ((parent (node-parent x))
(left? (eq? x (node-left parent)))
(w (node-child parent (not left?)))
(w (cond ((node-red? w)
(set-node-red?! w #f)
(set-node-red?! parent #t)
(rotate! parent left? tree)
(node-child (node-parent x) (not left?)))
(else
w))))
(cond ((and (node-black? (node-left w))
(node-black? (node-right w)))
(set-node-red?! w #t)
(loop (node-parent x)))
(else
(let ((w (cond ((node-black? (node-child w (not left?)))
(set-node-red?! (node-child w left?) #f)
(set-node-red?! w #t)
(rotate! w (not left?) tree)
(node-child (node-parent x) (not left?)))
(else
w))))
(let ((parent (node-parent x)))
(set-node-red?! w (node-red? parent))
(set-node-red?! parent #f)
(set-node-red?! (node-child w (not left?)) #f)
(rotate! parent left? tree)
(set-node-red?! (tree-root tree) #f)))))))))
; Verify that the coloring is correct
;
;(define (okay-tree? tree)
; (receive (okay? red? count)
; (let recur ((node (tree-root tree)))
; (if (not node)
; (values #t #f 0)
; (receive (l-ok? l-r? l-c)
; (recur (node-left node))
; (receive (r-ok? r-r? r-c)
; (recur (node-right node))
; (values (and l-ok?
; r-ok?
; (not (and (node-red? node)
; (or l-r? r-r?)))
; (= l-c r-c))
; (node-red? node)
; (if (node-red? node)
; l-c
; (+ l-c 1)))))))
; okay?))
;
;
;(define (walk-sequences proc list)
; (let recur ((list list) (r '()))
; (if (null? list)
; (proc (reverse r))
; (let loop ((list list) (done '()))
; (if (not (null? list))
; (let ((next (car list)))
; (recur (append (reverse done) (cdr list)) (cons next r))
; (loop (cdr list) (cons next done))))))))
;
;(define (tree-test n)
; (let ((iota (do ((i n (- i 1))
; (l '() (cons i l)))
; ((<= i 0) l))))
; (walk-sequences (lambda (in)
; (walk-sequences (lambda (out)
; (do-tree-test in out))
; iota))
; iota)
; #t))
;
;(define (do-tree-test in out)
; (let ((tree (make-search-tree = <)))
; (for-each (lambda (i)
; (search-tree-set! tree i (- 0 i)))
; in)
; (if (not (okay-tree? tree))
; (breakpoint "tree ~S is not okay" in))
; (if (not (tree-ordered? tree (length in)))
; (breakpoint "tree ~S is not ordered" in))
; (for-each (lambda (i)
; (if (not (= (search-tree-ref tree i) (- 0 i)))
; (breakpoint "looking up ~S in ~S lost" i in)))
; in)
; (do ((o out (cdr o)))
; ((null? o))
; (search-tree-set! tree (car o) #f)
; (if (not (okay-tree? tree))
; (breakpoint "tree ~S is not okay after deletions ~S" in out)))))
;
;(define (tree-ordered? tree count)
; (let ((l '()))
; (walk-search-tree (lambda (key value)
; (set! l (cons (cons key value) l)))
; tree)
; (let loop ((l l) (n count))
; (cond ((null? l)
; (= n 0))
; ((and (= (caar l) n)
; (= (cdar l) (- 0 n)))
; (loop (cdr l) (- n 1)))
; (else #f)))))
;
;(define (do-tests tester)
; (do ((i 0 (+ i 1)))
; (#f)
; (tester i)
; (format #t " done with ~D~%" i)))
;
;(define (another-test n)
; (let ((iota (do ((i n (- i 1))
; (l '() (cons i l)))
; ((<= i 0) l))))
; (walk-sequences (lambda (in)
; (do ((i 1 (+ i 1)))
; ((> i n))
; (let ((tree (make-search-tree = <)))
; (for-each (lambda (i)
; (search-tree-set! tree i (- 0 i)))
; in)
; (if (not (okay-tree? tree))
; (breakpoint "tree ~S is not okay" in))
; (if (not (tree-ordered? tree (length in)))
; (breakpoint "tree ~S is not ordered" in))
; (for-each (lambda (i)
; (if (not (= (search-tree-ref tree i) (- 0 i)))
; (breakpoint "looking up ~S in ~S lost" i in)))
; in)
; (search-tree-set! tree i #f)
; (if (not (okay-tree? tree))
; (breakpoint "tree ~S is not okay after deletion ~S"
; in i))
; (for-each (lambda (j)
; (let ((ref (search-tree-ref tree j)))
; (if (not (eq? ref (if (= j i) #f (- 0 j))))
; (breakpoint "looking up ~S in ~S lost" i in))))
; in))))
; iota)))