Maxiphobic Heaps
September 28, 2010
A maxiphobic heap is a binary tree with node defined as four-slot vectors:
(define mh-node vector) ; size item lkid rkid
Four functions provide access to the four slots of the vector; they are defined as macros so they will be inlined:
(define-syntax mh-size (syntax-rules () ((_ mh) (vector-ref mh 0))))
(define-syntax mh-item (syntax-rules () ((_ mh) (vector-ref mh 1))))
(define-syntax mh-lkid (syntax-rules () ((_ mh) (vector-ref mh 2))))
(define-syntax mh-rkid (syntax-rules () ((_ mh) (vector-ref mh 3))))
There is a single, distinguished node representing the empty priority queue, with size zero, and a predicate to identify it:
(define mh-empty (mh-node 0 'mh-empty 'mh-empty 'mh-empty))
(define (mh-empty? mh) (eqv? mh mh-empty))
The fundamental operation is merge:
(define (mh-merge lt? mh1 mh2)
(cond ((mh-empty? mh1) mh2) ((mh-empty? mh2) mh1)
(else (if (lt? (mh-item mh1) (mh-item mh2))
(let ((mhs (mh-biggest (mh-lkid mh1) (mh-rkid mh1) mh2)))
(mh-node (+ (mh-size mh1) (mh-size mh2))
(mh-item mh1) (car mhs)
(apply mh-merge lt? (cdr mhs))))
(let ((mhs (mh-biggest mh1 (mh-lkid mh2) (mh-rkid mh2))))
(mh-node (+ (mh-size mh1) (mh-size mh2))
(mh-item mh2) (car mhs)
(apply mh-merge lt? (cdr mhs))))))))
The merge function first checks for the trivial cases where one or the other maxiphobic heap is empty. If not, it compares the items at the roots of the two trees and merges them according to the algorithm. Merge calls an auxiliary function mh-biggest
to determine which sub-trees to merge first:
(define (mh-biggest mh1 mh2 mh3)
(if (< (mh-size mh1) (mh-size mh2))
(if (< (mh-size mh2) (mh-size mh3))
(list mh3 mh1 mh2)
(list mh2 mh1 mh3))
(if (< (mh-size mh1) (mh-size mh3))
(list mh3 mh1 mh2)
(list mh1 mh2 mh3))))
Given merge, insert, find-min and delete are trivial, and are identical to their leftist-heap counterparts:
(define (mh-insert lt? x mh)
(mh-merge lt? (mh-node 1 x mh-empty mh-empty) mh))
(define (mh-find-min mh)
(if (mh-empty? mh)
(error 'mh-find-min "empty maxiphobic heap")
(mh-item mh)))
(define (mh-delete-min lt? mh)
(if (mh-empty? mh)
(error 'mh-delete-min "empty maxiphobic heap")
(mh-merge lt? (mh-lkid mh) (mh-rkid mh))))
To illustrate maxiphobic heaps, we provide this sorting function that inserts each of its inputs into a maxiphobic heap, in random input order, and outputs them in priority order:
(define (mh-sort lt? xs)
(let loop1 ((xs xs) (mh mh-empty))
(if (pair? xs)
(loop1 (cdr xs) (mh-insert lt? (car xs) mh))
(let loop2 ((mh mh) (zs '()))
(if (mh-empty? mh)
(reverse zs)
(loop2 (mh-delete-min lt? mh)
(cons (mh-find-min mh) zs)))))))
Here’s an example:
> (mh-sort > '(3 1 7 2 5 6 4))
(7 6 5 4 3 2 1)
You can run the program at http://programmingpraxis.codepad.org/YZIkmZ02.
Here’s a ruby version from the psuedo code here (http://www.eecs.usma.edu/webs/people/okasaki/sigcse05.pdf)
It works, but it seems like it could be improved quite a bit though.
As a side note, the ruby version should work with any objects that support “” for sorting as in …