## Treaps

### June 26, 2009

We represent a treap as a tree of nodes, each a five-slot vector with places for the key, priority, value, and the two child nodes; access functions are given below:

`(define (treap k p v l r) (vector k p v l r))`

(define (key t) (vector-ref t 0))

(define (prio t) (vector-ref t 1))

(define (val t) (vector-ref t 2))

(define (lkid t) (vector-ref t 3))

(define (rkid t) (vector-ref t 4))

(define nil (vector 'nil -1 'nil 'nil 'nil))

(define (nil! k) (vector-set! nil 0 k))

(define (nil? t) (eq? t nil))

(define (leaf? t) (and (nil? (lkid t)) (nil? (rkid t))))

(define (leaf-or-nil? t) (eq? (lkid t) (rkid t)))

Rotations are done by building new nodes using the appropriate pieces of the old nodes:

`(define (rot-left t)`

(let ((l (treap (key t) (prio t) (val t) (lkid t) (lkid (rkid t)))))

(treap (key (rkid t)) (prio (rkid t)) (val (rkid t)) l (rkid (rkid t)))))

`(define (rot-right t)`

(let ((r (treap (key t) (prio t) (val t) (rkid (lkid t)) (rkid t))))

(treap (key (lkid t)) (prio (lkid t)) (val (lkid t)) (lkid (lkid t)) r)))

`Lookup`

is exactly the same as a standard binary search tree; the priority field is unused. Starting at the root, `lookup`

compares the key being sought to the current key, branching either left or right depending on the outcome of the comparison. `Lookup`

terminates with failure if it reaches `nil`

, and terminates with success if the compare returns equal.

`(define (lookup lt? t k)`

(cond ((nil? t) #f)

((lt? k (key t)) (lookup lt? (lkid t) k))

((lt? (key t) k) (lookup lt? (rkid t) k))

(else (cons k (val t)))))

`Insert`

works in two phases; a winding phase descends the tree to find the insertion point, in a manner similar to `lookup`

, and an unwinding phase performs rotations to restore the heap-order property:

`(define (insert lt? t k v)`

(cond ((nil? t) (treap k (rand) v nil nil))

((lt? k (key t))

(let ((t (treap (key t) (prio t) (val t) (insert lt? (lkid t) k v) (rkid t))))

(if (< (prio t) (prio (lkid t))) (rot-right t) t)))

((lt? (key t) k)

(let ((t (treap (key t) (prio t) (val t) (lkid t) (insert lt? (rkid t) k v))))

(if (< (prio t) (prio (rkid t))) (rot-left t) t)))

(else (treap k (prio t) v (lkid t) (rkid t)))))

`Delete`

begins by setting the desired key in `nil`

as a sentinel, then searches, rebuilding nodes as it proceeds, until the key being deleted is found (the search will always succeed because of the sentinel). Then, `delete`

calls an auxiliary procedure, `deroot`

, which rotates the current node down until it becomes a leaf, where it is clipped off. `Deroot`

selects rotations based on priority:

`(define (deroot t)`

(cond ((leaf-or-nil? t) nil)

((< (prio (lkid t)) (prio (rkid t)))

(let ((t (rot-left t)))

(treap (key t) (prio t) (val t) (deroot (lkid t)) (rkid t))))

(else (let ((t (rot-right t)))

(treap (key t) (prio t) (val t) (lkid t) (deroot (rkid t)))))))

`(define (delete lt? t k)`

(nil! k)

(let delete ((t t))

(cond ((lt? k (key t))

(treap (key t) (prio t) (val t) (delete (lkid t)) (rkid t)))

((lt? (key t) k)

(treap (key t) (prio t) (val t) (lkid t) (delete (rkid t))))

(else (deroot t)))))

`Update`

is a simple variant on `insert`

:

`(define (update lt? t f k v)`

(cond ((nil? t) (treap k (rand) v nil nil))

((lt? k (key t))

(let ((t (treap (key t) (prio t) (val t) (update lt? (lkid t) k (rand) v) (rkid t))))

(if (< (prio t) (prio (lkid t))) (rot-right t) t)))

((lt? (key t) k)

(let ((t (treap (key t) (prio t) (val t) (lkid t) (update lt? (rkid t) k (rand) v))))

(if (< (prio t) (prio (rkid t))) (rot-left t) t)))

(else (treap k (prio t) (f k (val t)) (lkid t) (rkid t)))))

`Enlist`

traverses the tree in order, calling itself recursively at each node:

`(define (enlist t)`

(if (nil? t) '()

(append (enlist (lkid t)) (list (cons (key t) (val t))) (enlist (rkid t)))))

An example of the use of treaps is given by the earlier word-frequency exercise, which counted the most frequently-used words in the Bible. Here is a restatement of that exercise using treaps:

`(define (word-freq n file-name)`

(define (freq-gt? a b) (> (cdr a) (cdr b)))

(with-input-from-file file-name

(lambda ()

(let loop ((word (read-word)) (freqs nil))

(if (eof-object? word)

(take n (sort freq-gt? (enlist freqs)))

(loop (read-word) (update string<? freqs (lambda (k v) (+ v 1)) word 1)))))))

The result is the same as the prior exercise:

`> (word-freq 25 "bible.txt")`

(("the" . 62588) ("and" . 30875) ("of" . 30183)

("to" . 23023) ("you" . 14887) ("in" . 13357) ("he" . 10495)

("a" . 10150) ("i" . 9078) ("for" . 8983) ("his" . 8424)

("lord" . 8129) ("your" . 7398) ("with" . 7259)

("that" . 7187) ("is" . 7143) ("they" . 7005) ("not" . 6484)

("him" . 6140) ("will" . 6093) ("them" . 5831) ("be" . 5668)

("who" . 5611) ("from" . 5476) ("it" . 5395))

`Rand`

comes from the Standard Prelude. Treaps were invented by Cecilia Aragon and Raimund Seidel, and described in two papers. You can see the code from this exercise at http://programmingpraxis.codepad.org/iFm0jW0A.

Pages: 1 2

[…] Praxis – Treaps By Remco Niemeijer Today’s Programming Praxis problem is about Treaps – binary trees that are more or less balanced thanks […]

My Haskell solution (see http://bonsaicode.wordpress.com/2009/06/26/programming-praxis-treaps/ for a version with comments):

Attempt in clojure , using pseudocode from http://sims.berkeley.edu/~aragon/pubs/rst96.pdf

pastebin link http://clojure.pastebin.com/f20e92d3a

Finally finished. It took me a while to get everything working since I went all out and implemented the full IDictionary interface. It was fun to get all of the code to work properly, but there’s clearly some optimization work needed to make the thing useful. My implementation’s performance compares unfavorably to the .Net framework’s built in System.Collections.Generic.Dictionary in all of my tests.

http://inscrutable.pastebin.com/f3fabfc9a

My implementation in C