Motivation
- Imagine implementing a balanced binary search tree (BST) without needing any rotations.
- Now imagine an array where you can:
- Insert or delete an element at any index in O(log n).
- Reverse a sub-array in O(log n).
- Swap two sub-arrays in O(log n).
Note: While this article focuses on a Treap implementation, other data structures can achieve similar tasks.
Introduction
The Treap is a binary search tree where each node has a randomly assigned numeric priority. You can learn more from Wikipedia.
Definitions
- Right tree (r): All keys are greater than a specific key.
- Left tree (l): All keys are less than or equal to a specific key.
Implementation Details
This section outlines a basic Treap implementation with the following operations:
- Split: Given a tree and key, returns two trees l and r.
- Insert: Inserts a node into the tree.
- Merge: Combines two trees (l and r) back into one.
- Erase: Removes a key from the tree.
- Reverse: Swaps the left and right children at each node in the tree.
Data Structure
struct Node {
int priority;
int key;
Node* l;
Node* r;
Node() : priority(0), key(0), l(NULL), r(NULL) {}
Node(int priority, int key) : priority(priority), key(key), l(NULL), r(NULL) {}
};
Each node holds a key (for BST ordering), a priority (for heap ordering), and pointers to its left (l
) and right (r
) subtrees. Use a random number generator, like rand()
in C++, for priority values.
Split Function
void split(Node* root, int key, Node*& l, Node*& r) {
if (!root)
l = r = NULL;
else if (key < root->key) {
r = root;
split(root->l, key, l, root->l);
} else {
l = root;
split(root->r, key, root->r, r);
}
}
The split function divides the tree into two: nodes ≤ key go to l
, and nodes > key go to r
.
Insert Function
Node* insert(Node* root, Node* node) {
if (!root)
return node;
if (node->priority > root->priority) {
split(root, node->key, node->l, node->r);
return node;
}
if (node->key < root->key)
root->l = insert(root->l, node);
else
root->r = insert(root->r, node);
return root;
}
The insert function finds the right spot for a node based on priority and key, preserving both BST and heap properties.
Merge Function
void merge(Node*& root, Node* l, Node* r) {
if (!l || !r)
root = l ? l : r;
else if (l->priority > r->priority) {
root = l;
merge(root->r, l->r, r);
} else {
root = r;
merge(root->l, l, r->l);
}
}
The merge function combines two trees into one, respecting the heap priority rules.
Erase Function
void erase(Node*& root, int key) {
if (!root)
return;
if (root->key == key)
merge(root, root->l, root->r);
else if (key < root->key)
erase(root->l, key);
else
erase(root->r, key);
}
To erase a node, find it and merge its left and right subtrees.
References
- 2016 Camp at 72B El Manial Street (explained by Coach Fegla).
- E-maxx.ru (implementation details in Russian).