Skip Lists & Treaps
Skip List ও Treap
1. Randomisation Replaces Rotations
AVL and Red-Black trees achieve guaranteed O(log n) using rotations + colour bookkeeping. Skip lists and treaps achieve expected O(log n) using random coin flips — no rotations to think about, no colour invariants. The code is shorter and the constant factor is often better.
2. Skip List — Linked Lists at Multiple Levels
A skip list is a sorted linked list where each node optionally appears in higher levels. With probability 1/2, each node is also in level 1; with 1/4, in level 2; and so on. Search descends from the top: at each level, walk forward as long as the next value ≤ target. Expected height is O(log n), expected search O(log n).
#include <bits/stdc++.h>
using namespace std;
struct SkipList {
struct Node { int v; vector<Node*> nxt; };
int maxLevel;
Node* head;
mt19937 rng;
SkipList(int ml = 16) : maxLevel(ml), rng(42) {
head = new Node{INT_MIN, vector<Node*>(ml, nullptr)};
}
int randomLevel() {
int lvl = 1;
while ((rng() & 1) && lvl < maxLevel) lvl++;
return lvl;
}
void insert(int v) {
vector<Node*> upd(maxLevel, head);
Node* cur = head;
for (int i = maxLevel - 1; i >= 0; i--) {
while (cur->nxt[i] && cur->nxt[i]->v < v) cur = cur->nxt[i];
upd[i] = cur;
}
int lvl = randomLevel();
Node* node = new Node{v, vector<Node*>(lvl, nullptr)};
for (int i = 0; i < lvl; i++) {
node->nxt[i] = upd[i]->nxt[i];
upd[i]->nxt[i] = node;
}
}
bool contains(int v) {
Node* cur = head;
for (int i = maxLevel - 1; i >= 0; i--)
while (cur->nxt[i] && cur->nxt[i]->v < v) cur = cur->nxt[i];
return cur->nxt[0] && cur->nxt[0]->v == v;
}
};
int main() {
SkipList sl;
for (int x : {3, 7, 10, 14, 22, 25, 31, 40}) sl.insert(x);
cout << sl.contains(22) << " " << sl.contains(9);
}
3. Treap — BST by Key, Heap by Priority
A treap assigns each inserted key a random priority. The structure is a BST on keys and a heap on priorities. Random priorities ⇒ random tree shape ⇒ expected height O(log n). Insertion is BST-insert, then rotations to restore the heap property — but you can equivalently implement it via split / merge, which is cleaner.
split(t, key) → (lo, hi): lo contains all nodes with key < given, hi the rest.
merge(lo, hi): combine assuming all keys in lo < all keys in hi.
Insert and erase are 1–2 splits + a merge.
4. Implicit Treap — Treat Position as Key
If we use the in-order position (rather than a value) as the implicit key, treap becomes a powerful sequence container: insert at position k, erase at position k, range-reverse, range-sum — all in O(log n). This is what powers many hard ICPC problems where you need to simulate sequence edits.
5. Where They Live in the Real World
| Structure | Used by | Why |
|---|---|---|
| Skip list | Redis ZSET, LevelDB MemTable | Easy concurrent insert; no rotations |
| Treap | ICPC contest libraries | Short code, split/merge for sequences |
| Implicit treap | Codeforces hard problems | Range insert / erase / reverse in O(log n) |
6. Practice Problems
-
Compute the expected height of a skip list of n nodes when each level is taken with probability ½.Expected height কীভাবে log₂ n হয়?
✨ Show Answer (উত্তর দেখুন)
Answer: probability that a node reaches level h is (½)ʰ. Expected number of nodes at level h is n / 2ʰ. The maximum non-empty level satisfies n/2ʰ ≥ 1, so h ≤ log₂ n. Tail bounds make actual height O(log n) w.h.p.
-
Given priorities P = [50, 30, 80, 10, 20] for keys [4, 2, 8, 1, 5] inserted in this order, draw the resulting treap.দেওয়া (key, priority) pair-এর treap আঁকুন।
✨ Show Answer (উত্তর দেখুন)
Answer: root is the priority-max = 80 (key 8). Its left subtree contains keys < 8 with their own priority-max as root: P among {4, 2, 1, 5} largest is 50 (key 4). Recurse → final tree: root 8(80) → left 4(50) → (left 2(30) → (left 1(10), right —), right 5(20)).
-
Sketch
split(treap, key)in pseudocode using recursion.Treap split-এর recursion।✨ Show Answer (উত্তর দেখুন)
split(t, key): if t is null: return (null, null) if t.key < key: (lo, hi) = split(t.right, key) t.right = lo return (t, hi) else: (lo, hi) = split(t.left, key) t.left = hi return (lo, t) -
Implicit treap — kth element retrieval. Sketch the descent.Implicit treap-এ kth element।
✨ Show Answer (উত্তর দেখুন)
Sketch: store
sizeat each node. Descend from root: if k < size(left) → go left; else if k == size(left) → return root; else → go right with k = k − size(left) − 1. O(log n) expected. -
Why does Redis pick a skip list for sorted-set, when a Red-Black tree would also give O(log n)?Redis Red-Black tree-এর বদলে skip list বেছে নেয় কেন?
✨ Show Answer (উত্তর দেখুন)
Answer: (1) Range queries (ZRANGEBYSCORE) are trivial — walk the bottom-level linked list. (2) Implementation is dramatically simpler — no rotations, no parent pointers, no rebalancing on every write. (3) Cache-friendlier for forward iteration. The constant factor and engineering simplicity matter more than asymptotic differences here.
Summary — Module 25
Randomised structures get rid of rotation bookkeeping. Skip lists put randomness into the number of "express lanes" each node joins; treaps put it into priorities. Both run in expected O(log n). Skip lists power Redis sorted sets; implicit treaps unlock sequence problems unreachable with arrays. Phase 5 complete — graphs await.