Splay Tree is a self-balancing binary search tree developed by Daniel Sleator and Robert Tarjan in 1985.
The key idea: every time a node is accessed, it is moved to the root of the tree using a splay operation (a series of rotations). It does not store height, color, or other metadata â only parent/left/right pointers.
class SplayNode:
def __init__(self, key, value=None):
self.key = key
self.value = value
self.left = None # left subtree (keys < key)
self.right = None # right subtree (keys > key)
self.parent = None # parent
Splay moves node x to the root through three rotation cases:
Inserting keys: 10, 20, 5, 15, 30 and then performing find(5)
find(5) â node 5 moves to the root:| Application | Why Splay Tree? |
|---|---|
| Browser Cache | Recently opened URLs are likely needed again â they are at the root |
| Text Editor | Recent edits are local â cursor position is found quickly |
| Network Router | "Hot" IP addresses are constantly at the top |
| Windows NT Registry | Frequently read keys bubble to the top |
| Garbage Collector (JVM) | Searching for free blocks by size |
| Link-Cut Trees | Splay is the building block for dynamic trees |
| Operation | Worst Case | Amortized | Best Case |
|---|---|---|---|
| Find | O(n) | O(log n) | O(1) |
| Insert | O(n) | O(log n) | O(1) |
| Delete | O(n) | O(log n) | O(1) |
| Split | O(n) | O(log n) | O(log n) |
| Merge | O(n) | O(log n) | O(log n) |
| k-th element | O(n) | O(log n) | O(1) |
â ī¸ Worst case O(n) for a single operation, but a series of m operations is always ⤠O(m log n)
| â Advantages | â Disadvantages |
|---|---|
| Simple implementation | Worst-case O(n) |
| Adapts to access patterns | Not for real-time |
| Excellent cache locality | Poor for uniform access |
| Split/Merge in O(log n) | Not persistent |
| Good for skewed access | find modifies the tree |
| Characteristic | Splay Tree | B-Tree | B+Tree |
|---|---|---|---|
| Structure | Binary | M-ary | M-ary |
| Data Storage | All nodes | All nodes | Leaves only |
| Optimization | RAM / Cache | Disk | Disk + Range |
| Balancing | Amortized | Strict | Strict |
| Tree Height | O(log n) amort. | O(log_m n) | O(log_m n) |
| Locality of reference | â Excellent | â None | â None |
| Range queries | â ī¸ Average | â ī¸ Average | â Excellent |
| Sequential scan | â Poor | â ī¸ Average | â Excellent |
| Memory Overhead | â Minimal | â ī¸ Average | â ī¸ Average |
| Node Metadata | â None | Degree/count | Degree/count |
| Worst-case 1 op | â O(n) | â O(log n) | â O(log n) |
| Application | Cache, RAM | FS, DB Index | DBMS, OS |
{
"title": { "text": "Relative Performance by Access Pattern", "left": "center" },
"tooltip": { "trigger": "axis", "axisPointer": { "type": "shadow" } },
"legend": { "top": 30, "data": ["Splay Tree", "B-Tree", "B+Tree"] },
"xAxis": {
"type": "category",
"data": ["Random", "Local (80/20)", "Sequential", "Range Scan", "Stream Insertion"]
},
"yAxis": {
"type": "value",
"name": "Score (higher = better)",
"max": 100
},
"series": [
{
"name": "Splay Tree",
"type": "bar",
"data": [55, 95, 30, 45, 60],
"itemStyle": { "color": "#4CAF50" }
},
{
"name": "B-Tree",
"type": "bar",
"data": [75, 65, 70, 70, 75],
"itemStyle": { "color": "#2196F3" }
},
{
"name": "B+Tree",
"type": "bar",
"data": [70, 60, 95, 98, 80],
"itemStyle": { "color": "#FF9800" }
}
]
}
class SplayTree:
def _right_rotate(self, x):
p = x.parent
b = x.right
# x goes up, p goes down to the right
if p.parent:
if p.parent.left == p: p.parent.left = x
else: p.parent.right = x
x.parent = p.parent
x.right = p
p.parent = x
p.left = b
if b: b.parent = p
def _left_rotate(self, x):
p = x.parent
b = x.left
if p.parent:
if p.parent.left == p: p.parent.left = x
else: p.parent.right = x
x.parent = p.parent
x.left = p
p.parent = x
p.right = b
if b: b.parent = p
def splay(self, x):
while x.parent: # until x is root
p = x.parent
g = p.parent # grandparent
if not g:
# Zig
if p.left == x: self._right_rotate(x)
else: self._left_rotate(x)
elif g.left == p and p.left == x:
# Zig-Zig (left-left)
self._right_rotate(p)
self._right_rotate(x)
elif g.right == p and p.right == x:
# Zig-Zig (right-right)
self._left_rotate(p)
self._left_rotate(x)
else:
# Zig-Zag
if p.left == x: self._right_rotate(x); self._left_rotate(x)
else: self._left_rotate(x); self._right_rotate(x)
self.root = x
def find(self, key):
node = self.root
last = None
while node:
last = node
if key == node.key:
self.splay(node) # found â move to root
return node
elif key < node.key: node = node.left
else: node = node.right
if last: self.splay(last) # not found â splay the last visited node
return None
def insert(self, key, value=None):
if not self.root:
self.root = SplayNode(key, value); return
node = self.root
while True:
if key < node.key:
if not node.left:
node.left = SplayNode(key, value)
node.left.parent = node
self.splay(node.left); return
node = node.left
elif key > node.key:
if not node.right:
node.right = SplayNode(key, value)
node.right.parent = node
self.splay(node.right); return
node = node.right
else:
node.value = value # update
self.splay(node); return
def delete(self, key):
node = self.find(key)
if not node: return
# After splay â node is at the root
left, right = self.root.left, self.root.right
if left: left.parent = None
if right: right.parent = None
if not left:
self.root = right
else:
# find maximum in the left subtree â becomes new root
self.root = left
m = left
while m.right: m = m.right
self.splay(m) # m â root of the left part
self.root.right = right
if right: right.parent = self.root
This is the primary theoretical strength of Splay Trees:
If element
xwas last requestedtoperations ago, the next access toxwill take O(log t)
In practice: frequently used elements live close to the root and are found faster â the structure automatically adapts to real access patterns.
| Scenario | Recommendation |
|---|---|
| Cache with a hot subset | â Splay Tree |
| Database, on-disk indexes | â B+Tree |
| File system | â B-Tree / B+Tree |
| In-memory with uniform access | â Red-Black Tree / AVL |
| Need worst-case guarantees | â AVL / RB / B-Tree |
| Dynamic trees (Link-Cut) | â Splay Tree |
| Multithreaded (many readers) | â Splay is inconvenient (write on read) |
đĄ Core Intuition: A Splay Tree is like a stack of papers on a desk: the ones you work with most often end up on top. No external organization â the structure adapts to your work on its own.