heap: Simple binary heaps for priority queues and so on.
authorMark Wooding <mdw@distorted.org.uk>
Tue, 3 Jul 2007 11:18:14 +0000 (12:18 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Tue, 3 Jul 2007 12:18:32 +0000 (13:18 +0100)
heap.lisp [new file with mode: 0644]
mdw.asd

diff --git a/heap.lisp b/heap.lisp
new file mode 100644 (file)
index 0000000..9f099c0
--- /dev/null
+++ b/heap.lisp
@@ -0,0 +1,193 @@
+;;; -*-lisp-*-
+;;;
+;;; $Id$
+;;;
+;;; Heap data structure; useful for priority queues and suchlike
+;;;
+;;; (c) 2006 Straylight/Edgeware
+;;;
+
+;;;----- Licensing notice ---------------------------------------------------
+;;;
+;;; This program is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 2 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with this program; if not, write to the Free Software Foundation,
+;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+(defpackage #:heap
+  (:use #:common-lisp)
+  (:export #:make-heap #:heap-count #:heap-empty-p
+          #:heap-insert #:heap-head #:heap-remove
+          #:heap-sort))
+(in-package #:heap)
+
+;;;--------------------------------------------------------------------------
+;;; Useful indexing functions.
+
+(declaim (inline parent left-child right-child))
+(deftype index () '(and unsigned-byte fixnum))
+(defun parent (i)
+  (declare (type index i))
+  (the index (floor (- i 1) 2)))
+(defun left-child (i)
+  (declare (type index i))
+  (the index (+ (* 2 i) 1)))
+(defun right-child (i)
+  (declare (type index i))
+  (the index (+ (* 2 i) 2)))
+
+;;;--------------------------------------------------------------------------
+;;; Low-level heap operations.
+
+(defun upheap (v key cmp n x)
+  "Insert the element X in the highest place possible in the heap."
+  (declare (type vector v)
+          (type function key cmp)
+          (type index n))
+  (let ((i n) (xk (funcall key x)))
+    (loop (when (zerop i) (return))
+         (let* ((j (parent i))
+                (y (aref v j)))
+           (when (funcall cmp (funcall key y) xk) (return))
+           (setf (aref v i) y
+                 i j)))
+    (setf (aref v i) x)))
+
+(defun downheap (v key cmp n x)
+  "Insert the element X in the lowest place possible in the heap."
+  (declare (type vector v)
+          (type function key cmp)
+          (type index n))
+  (let ((i 0) (xk (funcall key x)))
+    (loop (let ((l (left-child i))
+               (r (right-child i)))
+           (when (>= l n) (return))
+           (multiple-value-bind
+               (j y yk)
+               (let* ((y (aref v l))
+                      (yk (funcall key y)))
+                 (if (= r n)
+                     (values l y yk)
+                     (let* ((z (aref v r))
+                            (zk (funcall key z)))
+                       (if (funcall cmp yk zk)
+                           (values l y yk)
+                           (values r z zk)))))
+             (when (funcall cmp xk yk)
+               (return))
+             (setf (aref v i) y
+                   i j))))
+    (setf (aref v i) x)))
+
+(defun check (v key cmp n)
+  "Verify the heap invariant on the heap."
+  (declare (type vector v)
+          (type function key cmp)
+          (type index n))
+  (dotimes (i n)
+    (let* ((item (aref v i))
+          (item-key (funcall key item))
+          (l (left-child i))
+          (r (right-child i)))
+      (when (< l n)
+       (let ((left-item (aref v l)))
+         (assert (funcall cmp item-key (funcall key left-item))))
+       (when (< r n)
+         (let ((right-item (aref v r)))
+           (assert (funcall cmp item-key (funcall key right-item)))))))))
+
+;;;--------------------------------------------------------------------------
+;;; High-level heap things
+
+(defstruct (heap (:predicate heapp) (:constructor %make-heap))
+  "Data structure for a heap."
+  (v (make-array 16) :type vector)
+  (n 0 :type index)
+  (key #'identity :type function)
+  (compare #'<= :type function))
+
+(defun make-heap
+    (&key (compare #'<=) (key #'identity)
+          (type 't) (init-size 16) (contents nil contentsp))
+  "Return a new heap.
+
+   COMPARE is a partial-order predicate: (COMPARE X Y) should return true if
+   X <= Y in some order.
+
+   The TYPE is the element type of the heap.
+
+   INIT-SIZE is the initial allocation for the heap; the heap will grow
+   automatically if necessary, so this isn't a big deal.  This is only a
+   hint; make-heap may ignore it completely.
+
+   KEY is a function to extract the key from an element.  The default is to
+   use the item unmolested.
+
+   CONTENTS is the initial contents of the heap.  If omitted, the heap is
+   initially empty."
+  (let ((n (if contentsp (length contents) 0)))
+    (loop while (< init-size n)
+         do (setf init-size (ash init-size 1)))
+    (let ((v (make-array init-size :element-type type)))
+      (when contentsp
+       (reduce (lambda (i item)
+                 (upheap v key compare i item)
+                 (1+ i))
+               contents
+               :initial-value 0))
+      (%make-heap :compare compare :key key :n n :v v))))
+
+(defun heap-count (heap)
+  "Return the number of elements in HEAP."
+  (declare (type heap heap))
+  (heap-n heap))
+
+(defun heap-empty-p (heap)
+  "True if HEAP is empty."
+  (declare (type heap heap))
+  (zerop (heap-count heap)))
+
+(defun heap-insert (heap item)
+  "Insert ITEM into the HEAP."
+  (declare (type heap heap))
+  (let* ((v (heap-v heap))
+        (n (heap-n heap))
+        (sz (array-dimension v 0)))
+    (when (= n sz)
+      (setf v (adjust-array v (* 2 n))
+           (heap-v heap) v))
+    (upheap v (heap-key heap) (heap-compare heap) n item)
+    (setf (heap-n heap) (1+ n))))
+
+(defun heap-head (heap)
+  "Peep at the head item on HEAP."
+  (declare (type heap heap))
+  (assert (not (heap-empty-p heap)))
+  (aref (heap-v heap) 0))
+
+(defun heap-remove (heap)
+  "Remove the head item from HEAP and return it."
+  (declare (type heap heap))
+  (assert (not (heap-empty-p heap)))
+  (let ((v (heap-v heap))
+       (n (1- (heap-n heap))))
+    (prog1 (aref v 0)
+      (setf (heap-n heap) n)
+      (downheap v (heap-key heap) (heap-compare heap) n (aref v n)))))
+
+(defun heap-sort (items compare &key (key #'identity))
+  "Return the ITEMS, least-first, as sorted by the ordering COMPARE."
+  (let ((heap (make-heap :compare compare :contents items :key key)))
+    (loop repeat (heap-n heap)
+         collect (heap-remove heap))))
+
+;;;----- That's all, folks --------------------------------------------------
diff --git a/mdw.asd b/mdw.asd
index d9ed4c6..165fa3f 100644 (file)
--- a/mdw.asd
+++ b/mdw.asd
@@ -17,6 +17,7 @@
               (:file "collect" :depends-on ("mdw-base"))
               #+cmu (:file "unix" :depends-on ("mdw-base" "collect"))
               (:file "safely" :depends-on ("mdw-base"))
               (:file "collect" :depends-on ("mdw-base"))
               #+cmu (:file "unix" :depends-on ("mdw-base" "collect"))
               (:file "safely" :depends-on ("mdw-base"))
+              (:file "heap")
               (:file "aa-tree" :depends-on ("mdw-base"))
               (:file "infix")
               (:file "infix-ext" :depends-on ("mdw-base"
               (:file "aa-tree" :depends-on ("mdw-base"))
               (:file "infix")
               (:file "infix-ext" :depends-on ("mdw-base"