The partition function takes a new element to be added to the tree, which Okasaki calls the pivot element by analogy to quicksort, and returns two sub-trees containing elements smaller and bigger than the pivot, respectively:

This is complicated but correct. After handling an empty input tree, the two branches of the if handle the case where the top of the heap is less than the pivot (the then clause) or greater than or equal to the pivot (the else clause). Both clauses call partition recursively as they descend the tree. Notice the (heap (heap …)) in the first small clause, which is where the restructuring occurs.

Given partition, the three priority queue operators are simple. Insert partitions around the new element, then builds a new heap. First charges down the left spine of the tree until it finds the minimum element. Rest restructures the tree in the same manner as partition, except that there is no comparison because we always take the left path:

6 Responses to “Splay Heaps”

It’s not *that* different (I’m not sure how different two Scheme implementations could really be), but there is some. For example, I went with structs rather than wrapping a vector, which at once abstracts over the backend but unfortunately makes the function names a bit longer.

One thing I’m curious about is when you would want to use something like this. It was interesting to build it, but I feel like there would be better options, either for a priority queue (just use a list, insert in linked lists is relatively fast; although the runtime would be O(n) rather than amortized O(log n)) or for sorting (quicksort has a much simpler implementation, at least IMO). I guess if someone else writes the implementation and you just use it, then most of the issues would go away. Hmm.

@jpverkamp: We have used priority queues (heaps) in several exercises: Melissa O’Neill’s method of generating primes, apportioning votes in the U S House of Representatives, Prim’s algorithm for the minimum spanning tree, calculating the streaming median, building treaps, and in puzzle exercises from Wirth, Ullman and Amazon. You can find those by clicking on the Exercises | Search item on the menu bar. Priority queues are not just for sorting!

Perhaps I phrased that incorrectly. I definitely do see the use for priority queues in all sorts of cases. Personally, I remember using them for job scheduling in an industrial situation. I was more getting at this particular implementation (using a tree as the underlying structure rather than an array with power of two offsets).

Out of curiosity, I looked up what Java’s PriorityQueue and it’s apparently implemented using a heap. So I’ve used it several times without actually realizing it. :) That being said, their implementation uses a mutable array rather than a tree to implement the heap. So slightly different.

It’s based on the OpenJDK implementation of a PriorityQueue. It seems a bit more natural to me, although I wonder if modeling the tree explicitly rather than as an array (yet still performing the swaps) would have helped even more. It ended up taking me about as long to implement this one as the other, mostly because of a pair of really sneaky off-by-one errors that only cropped up in about 5% of randomized tests…

In Clojure. It is noticeably faster than a leftist heap. On my computer, to generate the first 1,000,000 primes using the leftist heap (O’Neil algorithm) takes 7.25 seconds. Replacing the heap used with a splay heap takes 6.4 seconds.