In the realm of data structures, self-balancing binary search trees (BSTs) are essential for maintaining efficient operations such as insertion, deletion, and lookup. One such structure is the Treap, which combines the properties of a binary search tree and a heap. This unique blend allows Treaps to offer average-case time complexity of O(log n) for these operations while maintaining simplicity and efficiency.
A Treap is a self-balancing binary search tree where each node has two attributes: a key (which follows the BST property) and a priority (which follows the heap property). The BST property ensures that for any given node, all keys in its left subtree are smaller, and all keys in its right subtree are larger. The heap property dictates that every node's priority is greater than or equal to the priorities of its children.
A Treap node can be represented as follows:
class TreapNode {
constructor(key, priority) {
this.key = key;
this.priority = priority;
this.left = null;
this.right = null;
}
}
Inserting a new node into a Treap involves two main steps: inserting it as if it were a regular BST and then performing rotations to maintain the heap property.
function insert(root, key, priority) {
if (!root) return new TreapNode(key, priority);
if (key < root.key) {
root.left = insert(root.left, key, priority);
if (root.priority < root.left.priority) {
root = rotateRight(root);
}
} else {
root.right = insert(root.right, key, priority);
if (root.priority < root.right.priority) {
root = rotateLeft(root);
}
}
return root;
}
function rotateRight(y) {
const x = y.left;
const T2 = x.right;
// Perform rotation
x.right = y;
y.left = T2;
// Return new root
return x;
}
function rotateLeft(x) {
const y = x.right;
const T2 = y.left;
// Perform rotation
y.left = x;
x.right = T2;
// Return new root
return y;
}
Deleting a node from a Treap involves finding the node, removing it, and then performing rotations to maintain the heap property.
function deleteNode(root, key) {
if (!root) return null;
if (key < root.key) {
root.left = deleteNode(root.left, key);
} else if (key > root.key) {
root.right = deleteNode(root.right, key);
} else {
// Node with only one child or no child
if (!root.left || !root.right) {
return root.left ? root.left : root.right;
}
// Node with two children: Get the inorder successor (smallest in the right subtree)
const temp = minValueNode(root.right);
root.key = temp.key;
root.priority = temp.priority;
root.right = deleteNode(root.right, temp.key);
}
return root;
}
function minValueNode(node) {
let current = node;
// Loop down to find the leftmost leaf
while (current.left !== null) {
current = current.left;
}
return current;
}
Lookup in a Treap is similar to a standard BST lookup, with an average time complexity of O(log n).
function search(root, key) {
if (!root || root.key === key) return root;
if (key < root.key) {
return search(root.left, key);
}
return search(root.right, key);
}
Treaps are used in various applications where efficient dynamic sets are required:
Treaps offer a simple yet powerful solution for maintaining balanced binary search trees. By combining the properties of BSTs and heaps, they provide efficient operations with average-case time complexity of O(log n). Understanding and implementing Treaps can significantly enhance your ability to design and optimize data structures for various applications.