home *** CD-ROM | disk | FTP | other *** search
- ; AVL-Bäume, implementiert in COMMON LISP
- ; Version für den Compiler, mit vielen Deklarationen
-
- ; Ein AVL-Baum ist ein Binärbaum, bei dem in jedem Knoten ein Datum
- ; (value) sitzt. Der Baum ist stets balanciert, in der Weise, daß die Höhen
- ; zweier linker und rechter Teilbäume sich um höchstens 1 unterscheiden.
- ; Die Ordnungsrelation auf den values ist durch eine Vergleichsfunktion comp
- ; festgelegt, die feststellt, wann x<y ist (eine fest gewählte
- ; Ordnungsrelation). Bei (not (or (comp x y) (comp y x))) gelten x und y als
- ; gleich.
-
- (provide 'avl)
- (in-package 'avl)
- (shadow '(member delete copy merge))
- (export '(treep member insert delete do-avl avl-to-seq seq-to-avl copy merge))
-
- (eval-when (compile) (proclaim '(optimize (speed 3))))
-
- ; Datenstruktur eines Baumes: leerer Baum=nil, sonst Knoten ("node")
-
- (deftype tree ()
- '(or null node))
-
- (defstruct node
- (level 0 :type fixnum)
- (left nil :type tree)
- (right nil :type tree)
- (value nil)
- )
- (proclaim '(inline node-level node-left node-right node-value))
-
- ; (level tree) ergibt die Höhe eines Baumes
- (proclaim '(function level (node) fixnum))
- (proclaim '(inline level))
- (defun level (tr)
- (if tr (locally (declare (type node tr)) (node-level tr)) 0)
- )
-
- ; (deftype avl-tree (comp) ...) funktioniert nicht.
-
- ; (treep tr comp) stellt fest, ob ein AVL-Baum vorliegt.
- (proclaim '(function treep (tree function &optional t) symbol))
- (defun treep (tr comp &optional (el-type t))
- (or (null tr)
- (and
- (typep tr 'node)
- (locally (declare (type node tr))
- (and
- (typep (node-value tr) el-type)
- (let ((trl (node-left tr))
- (trr (node-right tr)))
- (declare (type tree trl trr) (type node tr))
- (and (= (level tr)
- (1+ (the fixnum
- (max (the fixnum (level trl))
- (the fixnum (level trr))
- ) ) ) )
- (<= (the fixnum
- (abs (the fixnum
- (- (the fixnum (level trl))
- (the fixnum (level trr))
- ) ) ) )
- 1
- )
- (or (null trl)
- (locally (declare (type node tr trl))
- (funcall comp (node-value trl) (node-value tr))
- ) )
- (or (null trr)
- (locally (declare (type node tr trr))
- (funcall comp (node-value tr) (node-value trr))
- ) )
- (treep trl comp el-type)
- (treep trr comp el-type)
- ) ) ) ) ) ) )
-
-
- ; (ganzrechts tr) liefert das "größte" Element eines nichtleeren Baumes
- (proclaim '(function ganzrechts (node) node))
- (defun ganzrechts (tr)
- (declare (type node tr))
- (if (node-right tr) (ganzrechts (node-right tr)) (node-value tr)))
-
- (proclaim '(function ganzlinks (node) node))
- ; (ganzlinks tr) liefert das "kleinste" Element eines nichtleeren Baumes
- (defun ganzlinks (tr)
- (declare (type node tr))
- (if (node-left tr) (ganzlinks (node-left tr)) (node-value tr)))
-
-
- ; (member item tree comp) testet, ob item ein Element des Baumes tree ist.
- ; Durch Angabe eines Gleichheitstests eq-test kann geprüft werden, ob die
- ; beiden Werte (item und der Wert im Baum) in einem engeren Sinne gleich sind.
- ; Trick: Falls man im Baum keine values mit dem Wert NIL abspeichert, kann man
- ; sich durch eq-test = #'(lambda (it val) (and ("=" it val) val)) den im Baum
- ; stehenden Wert val zurückgeben lassen.
- (proclaim '(function member (t tree function &optional function) t))
- (defun member (item tr comp &optional (eq-test #'equal))
- (if (null tr) nil
- (locally (declare (type node tr))
- (cond ((funcall eq-test item (node-value tr)))
- ((funcall comp item (node-value tr))
- (member item (node-left tr) comp eq-test))
- ((funcall comp (node-value tr) item)
- (member item (node-right tr) comp eq-test))
- ) ) ) ) ; sonst NIL
-
-
- ; (balance tree) balanciert einen nichtleeren Baum tree aus. Voraussetzung
- ; ist, daß höchstens ein Element den Baum aus der Balance gebracht hat.
- ; tree selbst wird verändert!
- (proclaim '(function balance (node) node))
- (defun balance (b)
- (let ((l (level (node-left b)))
- (r (level (node-right b))))
- (declare (fixnum l r) (type node b c d))
- (setf (node-level b) (the fixnum (1+ (the fixnum (max l r)))))
- (case (the fixnum (- r l))
- ((-2)(let ((c (node-left b))
- (d nil))
- (cond ((< (the fixnum (level (node-left c)))
- (the fixnum (level (node-right c))))
- (setq d (node-right c))
- (setf (node-right c) (node-left d))
- (setf (node-left b) (node-right d))
- (setf (node-left d) c)
- (setf (node-right d) b)
- (setf (node-level b) (node-level d))
- (setf (node-level d) (node-level c))
- (setf (node-level c) (node-level b))
- d
- )
- (t
- (setf (node-left b) (node-right c))
- (setf (node-right c) b)
- (setf (node-level b)
- (the fixnum (1+ (the fixnum (level (node-left b))))))
- (setf (node-level c)
- (the fixnum (1+ (the fixnum (node-level b)))))
- c
- ) ) ) )
- ((2) (let ((c (node-right b))
- (d nil))
- (cond ((< (the fixnum (level (node-right c)))
- (the fixnum (level (node-left c))))
- (setq d (node-left c))
- (setf (node-left c) (node-right d))
- (setf (node-right b) (node-left d))
- (setf (node-right d) c)
- (setf (node-left d) b)
- (setf (node-level b) (node-level d))
- (setf (node-level d) (node-level c))
- (setf (node-level c) (node-level b))
- d
- )
- (t
- (setf (node-right b) (node-left c))
- (setf (node-left c) b)
- (setf (node-level b)
- (the fixnum (1+ (the fixnum (level (node-right b))))))
- (setf (node-level c)
- (the fixnum (1+ (the fixnum (node-level b)))))
- c
- ) ) ) )
- ((-1 0 1) b)
- ) ) )
-
-
- ; (insert item tree comp) fügt item zusätzlich in tree ein.
- ; Das Ergebnis ist ebenfalls ein AVL-Baum. Falls item bereits vorkommt,
- ; wird item an dessen Stelle eingesetzt.
- ; Durch Angabe eines Gleichheitstest eq-test kann angegeben werden, was
- ; für Elemente als gleich zu gelten haben. (Das muß diejenigen Elemente
- ; umfassen, die nicht vergleichbar sind: stets x<y oder y<x oder (eq-test x y).)
- ; tree selbst wird verändert!
- (proclaim '(function insert (t tree function &optional function) node))
- (defun insert (item tr comp &optional (eq-test #'equal))
- (if (null tr) (make-node :level 1 :value item)
- (locally (declare (type node tr))
- (cond
- ((funcall eq-test item (node-value tr))
- (setf (node-value tr) item)
- tr)
- (t
- (cond
- ((funcall comp item (node-value tr))
- (setf (node-left tr) (insert item (node-left tr) comp eq-test)))
- ((funcall comp (node-value tr) item)
- (setf (node-right tr) (insert item (node-right tr) comp eq-test)))
- (t (error "Element paßt nicht in AVL-Baum-Ordnung!"))
- )
- (balance tr)
- ) ) ) ) )
-
-
- ; (delete item tree comp) entfernt item aus tree und liefert das
- ; verkleinerte tree zurück.
- (proclaim '(function delete (t tree function &optional function) tree))
- (defun delete (item tr comp &optional (eq-test #'equal))
- (if (null tr) tr
- (locally (declare (type node tr))
- (cond
- ((funcall eq-test item (node-value tr))
- (let ((r (node-right tr)))
- (declare (type node tr))
- (if (null r)
- (node-left tr)
- (multiple-value-bind (rest del) (delete-ganzlinks r)
- (declare (type node del))
- (setf (node-left del) (node-left tr))
- (setf (node-right del) rest)
- (balance del)
- )) ) )
- ((funcall comp item (node-value tr))
- (setf (node-left tr) (delete item (node-left tr) comp eq-test))
- (balance tr))
- ((funcall comp (node-value tr) item)
- (setf (node-right tr) (delete item (node-right tr) comp eq-test))
- (balance tr))
- (t (error "Element paßt nicht in AVL-Baum-Ordnung!"))
- ) ) ) )
-
- ; (delete-ganzlinks tree) entfernt aus dem nichtleeren tree das "kleinste"
- ; Element und gibt den Restbaum zurück. Das entfernte Element erscheint als
- ; zweiter Wert (als Knoten, zur Vermeidung von Garbage Produktion).
- (proclaim '(function delete-ganzlinks (node) tree))
- (defun delete-ganzlinks (tr)
- (declare (type node tr))
- (if (null (node-left tr))
- (values (node-right tr) tr)
- (multiple-value-bind (tl el) (delete-ganzlinks (node-left tr))
- (setf (node-left tr) tl)
- (values tr el)
- ) ) )
-
-
- ; (do-avl (var treeform [resultform]) {declaration}* {tag|statement}* )
- ; ist ein Macro wie dolist: Für alle var aus dem AVL-Baum, der bei
- ; treeform herauskommt, wird der Rest ausgeführt.
- (defmacro do-avl (varform &rest body)
- `(progn
- (traverse ,(second varform)
- #'(lambda (,(first varform)) ,@body)
- )
- ,(cond ((third varform) `(let ((,(first varform) nil)) ,(third varform))))
- ) )
-
- (defmacro do-avl-1 ((var treeform &optional resultform) &body body)
- (let ((abstieg (gensym)) ; Labels
- (aufstieg (gensym))
- (ende (gensym))
- (stack (gensym)) ; (cons ,top ,stack) ist ein "Stack"
- (top (gensym)))
- `(prog ((,stack nil) (,top ,treeform))
- ,abstieg
- (if (null ,top) (go ,aufstieg))
- (push ,top ,stack) (setq ,top (node-left (the node ,top)))
- (go ,abstieg)
- ,aufstieg
- (if (null ,stack) (go ,ende))
- (if (eq ,top (node-right (the node (setq ,top (pop ,stack)))))
- (go ,aufstieg))
- (let ((,var (node-value (the node ,top)))) ,@body)
- (push ,top ,stack) (setq ,top (node-right (the node ,top)))
- (go ,aufstieg)
- ,ende
- (let ((,var nil)) (return ,resultform))
- )
- ) )
-
- (proclaim '(function traverse (tree (function (t) t)) null))
- (defun traverse (tr fun)
- (if (null tr) nil
- (locally (declare (type node tr))
- (traverse (node-left tr) fun)
- (funcall fun (node-value tr))
- (traverse (node-right tr) fun)
- ) ) )
-
-
- ; (avl-to-seq tree) ergibt eine sortierte Liste aller values des Baumes tree.
- ; (avl-to-seq tree seq-type) ergibt eine sortierte Sequence des angegebenen
- ; Typs aus allen Werten des Baumes tree.
- (proclaim '(function avl-to-seq (tree &optional t) sequence))
- (defun avl-to-seq (tr &optional (result-type 'list))
- (if (null tr)
- (make-sequence result-type 0)
- (locally (declare (type node tr))
- (concatenate result-type
- (avl-to-seq (node-left tr))
- (make-sequence result-type 1 :initial-element (node-value tr))
- (avl-to-seq (node-right tr))
- ) ) ) )
-
- ; (seq-to-avl l comp) ergibt aus einer (unsortierten) sequence l von Elementen
- ; einen AVL-Baum.
- (proclaim '(function seq-to-avl (sequence function &optional function) tree))
- (defun seq-to-avl (l comp &optional (eq-test #'equal))
- (reduce #'(lambda (tr item) (insert item tr comp eq-test))
- l :initial-value nil
- ) )
-
-
- ; (copy tree) ergibt eine Kopie des AVL-Baumes tree.
- ; Nur die Baumstruktur wird kopiert, die Werte werden übernommen.
- ; insert und delete sind jetzt auf dem Original und auf der Kopie unabhängig
- ; voneinander durchführbar.
- (proclaim '(function copy (tree) tree))
- (defun copy (tr)
- (if (null tr) nil
- (locally (declare (type node tr))
- (make-node :level (node-level tr)
- :left (copy (node-left tr))
- :right (copy (node-right tr))
- :value (node-value tr)
- ) ) ) )
-
-
- ; (merge tree1 tree2 comp) ergibt einen neuen AVL-Baum, der aus den Elementen
- ; der Bäume tree1 und tree2 besteht.
- ; Durch Angabe eines Gleichheitstests kann spezifiert werden, was für
- ; Elemente (weil gleich) nicht doppelt in den neuen AVL-Baum übernommen zu
- ; werden brauchen. (Je zwei nicht vergleichbare Elemente müssen in diesem
- ; Sinne gleich sein.)
- (proclaim '(function merge (tree tree function &optional function) tree))
- (defun merge (tr1 tr2 comp &optional (eq-test #'equal))
- (if (< (the fixnum (level tr1)) (the fixnum (level tr2))) (rotatef tr1 tr2))
- ; jetzt ist tr1 der größere der Bäume
- (let ((tr (copy tr1)))
- (do-avl (x tr2 tr) (setq tr (insert x tr comp eq-test)))
- ) )
-
-