The idea: a search tree and a heap at the same time

A treap (tree + heap) is a binary search tree in which every node also gets a random number, its priority. The keys follow the BST order: everything in the left subtree is smaller, everything in the right subtree is larger. The priorities follow the heap order: a parent's priority is at least as high as its children's (a max-heap), so the highest priority is the root.

The page's starting treap. Root 50 with priority 90; children 25 (70) and 75 (80); then 12 (40), 37 (60), 62 (30), 88 (55); and 30 (20) under 37. Read left to right the keys are sorted; read top to bottom the priorities only go down.
The starting treap: left to right the keys are sorted (BST), top to bottom the priorities only go down (max-heap).

On the canvas each node shows its key on top and its priority below. A node's x position is its rank among the keys, so a rotation only moves nodes up and down. Priorities are 1 to 99 and kept distinct; "random" comes from a seeded generator (seed 1), so every run is the same.

Rotations keep the keys in order

A rotation swaps a node x with its parent p: x moves up, p moves down to x's other side, and the one subtree in between (B) changes parent. The in-order sequence A x B p C does not change, so the BST order still holds. That is why rotations are the tool for fixing the heap order.

Left: p with priority 60 has left child x with priority 65 and subtrees A, B under x and C under p. Rotating right at p gives x on top with A on its left and p on its right, and p holds B and C. Rotating left at x goes back.
A rotation moves x above p and hands subtree B to p; the in-order sequence A x B p C stays the same.

Insert: a BST leaf, then rotate up

  1. Walk down by key as in a plain binary search tree and hang the new node as a leaf.
  2. Give it a random priority.
  3. While its priority is higher than its parent's, rotate it above the parent.

The new node stops as soon as its parent has a higher priority (Demo: insert 40 (priority 65), one rotation: 65 beats 37's 60 but not 25's 70). A very high priority climbs all the way to the root (Demo: insert 70 (priority 95), up to the root: three rotations).

Delete: rotate down, then cut the leaf

Find the node. While it has children, lift the child with the higher priority with a rotation: that child has to end up above its sibling anyway, so the heap order stays correct around it. The node sinks one level per rotation until it is a leaf, and then it is cut off (Demo: delete the root 50, rotate it down: four rotations, and 75 becomes the root).

Split and merge with a sentinel

split(k) cuts a treap into L (keys < k) and R (keys ≥ k). The trick: insert a sentinel with key k and priority ∞. It goes down as a leaf, then beats every priority and rotates up to the root. Now its left subtree is L and its right subtree is R; remove it (Demo: split at 45 with a sentinel).

The sentinel 45 with priority infinity is the root. Its left subtree, 25 with 12 and 37 and 30, is L with the keys below 45. Its right subtree, 50 with 75, 62 and 88, is R with the keys 45 and above.
After split(45) the sentinel is the root; removing it leaves L and R, both valid treaps.

merge(L, R), when every key of L is below every key of R, is the reverse: put a sentinel with priority −∞ on top, with L on its left and R on its right, rotate it down as in a delete, and cut it off (Demo: split at 45, then merge back: the merged treap is exactly the starting one). Both take time proportional to the depth, O(log n) expected. Many treap libraries write split and merge as short recursive functions instead, and build insert and delete from them: insert = split, then merge L, the new node and R.

Why the depth is O(log n)

For distinct priorities there is exactly one treap for a set of (key, priority) pairs: the highest priority must be the root, the keys smaller than it form the left subtree, and so on down. That tree is the same BST you would get by inserting the keys in order of decreasing priority. With random priorities that order is random, so the treap looks like a BST built from a random permutation: expected depth of a node about 2 ln n ≈ 1.39 log₂ n, whatever order the keys arrive in.

A plain BST fed sorted keys becomes a path of height n. A treap fed sorted keys stays shallow (Demo: sorted inserts 10 to 70, random priorities). An insert does on average fewer than 2 rotations, and a delete too.

Treap vs other balanced trees

TreapAVL / red-blackSkip list
BoundO(log n) expectedO(log n) worst caseO(log n) expected
Extra per nodeone priorityheight or coloura tower of pointers
Rebalancingrotations by priority, < 2 on averagerotations and recolouring by casenone, random heights
Split / mergesimple, O(log n)possible but fiddlysimple

Split and merge are why treaps are popular in practice: an implicit treap uses the subtree size instead of a stored key, so it can cut and join sequences by position (a rope), reverse ranges or answer range queries. See also splay trees, another self-adjusting BST built from rotations.

What the page leaves out

The page keeps at most 12 keys with integer priorities from 1 to 99 (real treaps use 32- or 64-bit random numbers, so ties practically never happen). It leaves out the recursive split and merge code, subtree sizes and implicit treaps, range queries and lazy updates, persistent treaps, union and intersection of two treaps, and the related Cartesian tree and zip tree.