Leftist Tree
A leftist tree is a mergeable heap-ordered binary tree invented by Clark A. Crane that maintains the shortest path to an external node along its rightmost spine, enabling O(log n) worst-case melding, insertion, and minimum extraction.
Leftist tree (or leftist heap) is a pointer-based mergeable priority queue data structure within computation and algorithms implemented as a heap-ordered binary tree that is intentionally unbalanced toward the left so that its rightmost path remains logarithmically short. Invented by Clark Allan Crane in his 1972 Stanford University doctoral dissertation on linear lists and priority queues and popularized by Donald Ervin Knuth in Volume 3 (Sorting and Searching, 1973) of The Art of Computer Programming, the leftist tree was the first priority queue to support merging (Meld) of two disjoint heaps of total size n in O(log n) worst-case time, compared with the ฮ(n) time required to merge two array-based binary heaps.
Rather than trying to keep the entire binary tree height-balanced like an AVL or red-black search tree, a leftist tree enforces a local structural inequality at every internal node x: the null-path length (or s-value / rank) s(left(x)) of its left child is at least as large as the null-path length s(right(x)) of its right child. Because every step down the right spine decreases s(x) by at least 1 and a subtree with null-path length k contains at least 2k โ 1 internal nodes, the rightmost path from the root to an external null link has length at most โlog2(n + 1)โ. Consequently, all priority queue updatesโMeld, Insert, and Delete-Minโcan perform their work entirely along the short right spines of the participating trees and restore the leftist invariant on the way back up via simple left-right child swaps, laying the foundation for later self-adjusting and amortized mergeable heaps including the skew heap, pairing heap, Fibonacci heap, and Brodal queue.
Null-Path Length and the Leftist Invariant
Definition of s(x) (Distance to the Nearest External Node)
In an extended binary tree where every missing child pointer is represented as an explicit external node (null link โก), the null-path length (denoted s(x) by Knuth, for shortest path) of any node x is defined as the length (number of edges) of a shortest path from x downward to an external node:
- For any external null node โก, s(โก) = 0.
- For any internal node x with children left(x) and right(x):
A binary tree is a height-biased leftist tree (HBLT) if, at every internal node x, the null-path length of the left child is greater than or equal to the null-path length of the right child:
Combining this inequality with the recurrence for s(x) yields two immediate simplifications at every node x of a leftist tree:
- s(x) = 1 + s(right(x)), meaning the shortest path from x to an external node always follows the rightmost spine of the subtree rooted at x.
- While the left subtree of x may be arbitrarily deep (even forming a degenerate linear chain of length ฮ(n)), the right spine is guaranteed to be compact.
Logarithmic Bound on Right-Spine Length
Theorem (Crane, 1972; Knuth, 1973): If the subtree rooted at x in a leftist tree has null-path length s(x) = k, then that subtree contains at least 2k โ 1 internal nodes. Equivalently, in an n-node leftist tree, the length of the rightmost path from the root to an external node satisfies:
Proof: Because s(root) = k is the minimum distance from the root to any external node, every external node lies at depth at least k. Therefore, Levels 0, 1, โฆ, k โ 1 of the binary tree are completely filled with internal nodes, totaling 20 + 21 + โฆ + 2kโ1 = 2k โ 1 internal nodes. Since the tree has n internal nodes in total, n โฅ 2k โ 1, so k โค log2(n + 1), and because k is an integer, k โค โlog2(n + 1)โ.
In a leftist heap, every node x simultaneously obeys the min-heap order property (key[x] โค key[left(x)] and key[x] โค key[right(x)]) and the leftist property (s(left(x)) โฅ s(right(x))).
Meld-Driven Operations and Complexity Analysis
Recursive and Iterative Right-Spine Meld
The central primitive of a leftist heap is Meld(H1, H2), which merges two node-disjoint leftist heaps of sizes n1 and n2 (with n = n1 + n2) into a single valid leftist heap in O(log n) worst-case time:
- Base case: If H1 = โก, return H2; if H2 = โก, return H1.
- Root comparison: Compare key[H1] and key[H2]. Without loss of generality, assume key[H1] โค key[H2] (otherwise swap the roles of H1 and H2). The root of H1 becomes the root of the merged heap, and its entire left subtree left(H1) is left untouched.
- Recursive right-spine merge: Set right(H1) โ Meld(right(H1), H2).
- Leftist restoration: Compare s(left(H1)) and s(right(H1)). If s(left(H1)) < s(right(H1)), swap the two child pointers left(H1) โ right(H1).
- Rank update: Update s(H1) โ 1 + s(right(H1)) and return H1.
Because each recursive step advances down the right spine of either H1 or H2, the total number of comparisons and pointer updates is at most:
All other priority queue operations reduce directly to Meld:
- Find-Min(H): Return key[H] in O(1) worst-case time.
- Insert(H, x): Create a 1-node leftist tree with s(x) = 1 and call Meld(H, x) in O(log n) worst-case time.
- Delete-Min(H): Remove the root of H and return Meld(left(H), right(H)) in O(log n) worst-case time.
- Decrease-Key(H, x, k) (with parent pointers): Cut the subtree rooted at x from its parent p(x), walk upward from p(x) recomputing s(u) and swapping children as needed until an ancestor's s-value stops changing (at most O(log n) steps because each step moves up along a right child whose s-value was smaller), update key[x] โ k, and Meld(H, x) in O(log n) worst-case time.
| Mergeable Heap Variant | Balance Field per Node | Find-Min | Meld (Union) | Insert | Delete-Min | Persistence (Functional) |
|---|---|---|---|---|---|---|
| Binary Heap (Array) | None (implicit array) | O(1) | ฮ(n) | O(log n) | O(log n) | No (in-place array) |
| Height-Biased Leftist Tree (1972) | Null-path length s(x) (โค 6 bits) | O(1) | O(log n) worst | O(log n) worst | O(log n) worst | Yes (O(log n) path copy) |
| Weight-Biased Leftist Tree (1998) | Subtree weight w(x) | O(1) | O(log n) worst | O(log n) worst | O(log n) worst | Yes (single top-down pass) |
| Skew Heap (1986) | 0 bits (unconditional swap) | O(1) | O(log n) amort. | O(log n) amort. | O(log n) amort. | Amortized only |
| Binomial Heap (1978) | Degree k | O(log n) | O(log n) worst | O(1) amort. | O(log n) worst | Yes (Okasaki, 1998) |
O(n) Bottom-Up Heap Construction via FIFO Queue
Inserting n elements one by one into an initially empty leftist heap takes O(n log n) time. Knuth (1973) showed how to initialize an n-node leftist heap in O(n) worst-case time using a first-in, first-out (FIFO) queue Q:
- Place all n elements into Q as n individual 1-node leftist trees.
- While Q contains two or more trees, dequeue the first two trees T1 and T2 from the front of Q, compute T โ Meld(T1, T2), and enqueue T at the back of Q.
- Return the single remaining tree in Q.
In pass j โฅ 1, the algorithm pairs up โn / 2jโ trees of size 2jโ1, each meld taking at most O(j) time. Summing over all passes gives โj=1..โlog nโ (n / 2j) ยท O(j) = O(n).
Weight-Biased Leftist Trees and Persistent Applications
Weight-Biased Leftist Trees (Cho and Sahni, 1998)
Seonghun Cho and Sartaj Sahni (1998) introduced the weight-biased leftist tree (WBLT), which replaces the null-path length s(x) with the subtree weight w(x) (the total number of internal nodes in the subtree rooted at x, plus 1) and enforces w(left(x)) โฅ w(right(x)) at every node. Because the sum of weights of two merged trees w(H1) + w(H2) is known before recursing into the right child, a WBLT can decide whether to swap left and right children on the way down the right spine in a single top-down pass without needing a bottom-up return pass or stack frame unwind.
Purely Functional Persistence and Eppstein's k-Shortest Paths
Because Meld(H1, H2) in a leftist tree modifies only the O(log n) nodes along the right spines of H1 and H2 and never touches their left subtrees, allocating fresh copies of the O(log n) right-spine nodes during each Meld yields a fully persistent (purely functional) mergeable priority queue in O(log n) worst-case time and space per operation (unlike the skew heap or splay tree, whose amortized bounds break under persistence if an adversary repeatedly queries an expensive historical version).
This persistent O(log n) Meld is a foundational building block in David Eppstein's 1998 algorithm for finding the k shortest paths between two vertices in a directed graph in O(E + V log V + k) time: after computing a shortest-path tree via Dijkstra's algorithm, each vertex v shares a persistent leftist heap of sidetrack edges with its shortest-path parent by persistently inserting v's own outgoing non-tree edges into the parent's leftist heap in O(outdeg(v) + log E) time and space.
Frequently Asked Questions
How many bits per node are needed to store the null-path length s(x)?
Because s(x) โค โlog2(n + 1)โ, even a leftist tree containing n = 264 โ 1 nodes (spanning the entire 64-bit address space) has s(x) โค 64. Thus a single 6-bit or 8-bit unsigned integer field (uint8) inside each node suffices to store s(x) for any physically realizable tree.
Why is an arbitrary deletion or Decrease-Key harder in a leftist tree than in a binary heap?
In a standard binary heap, every path from a node to the root has length at most โlog2 nโ, so bubbling a decreased key up through its ancestors takes O(log n) time. In a leftist tree, only the rightmost spine is guaranteed to have length O(log n); a path that follows left child links can have length ฮ(n). Therefore, Decrease-Key cannot simply bubble a key up to the root by swapping values; instead, it must sever the subtree at x, update s-values up to the first ancestor where s remains unchanged (which traverses at most O(log n) right links), and Meld the severed subtree back into the root.
How does a leftist tree compare to a skew heap in practice?
A skew heap omits the s(x) field and swaps the left and right children of every node visited along the right spine during Meld. While a skew heap saves 1 byte of storage per node and avoids comparing s-values, a leftist tree performs child swaps only when s(left(x)) < s(right(x)), guarantees worst-case rather than merely amortized O(log n) time, and supports worst-case persistent path copying without degradation under repeated branching queries.
References
- Crane, C. A. (1972). Linear Lists and Priority Queues as Balanced Binary Trees. Technical Report STAN-CS-72-259 (Ph.D. dissertation), Computer Science Department, Stanford University. https://i.stanford.edu/pub/cstr/reports/cs/tr/72/259/CS-TR-72-259.pdf
- Knuth, D. E. (1973). The Art of Computer Programming, Volume 3: Sorting and Searching (Section 5.2.3, pp. 150โ152). Reading, MA: Addison-Wesley. https://www-cs-faculty.stanford.edu/~knuth/taocp.html
- Cho, S., & Sahni, S. (1998). "Weight biased leftist trees and modified skip lists." ACM Journal of Experimental Algorithmics, 3, Article 2. https://doi.org/10.1145/297096.297111
- Eppstein, D. (1998). "Finding the k shortest paths." SIAM Journal on Computing, 28(2), 652โ673. https://doi.org/10.1137/S0097539795290477
- Tarjan, R. E. (1983). Data Structures and Network Algorithms (Chapter 3: Heaps, pp. 33โ43). Philadelphia, PA: SIAM. https://doi.org/10.1137/1.9781611970265