Treap树堆C++实现
Treap
- Treap
树堆(Treap = Tree + Heap)
,指的是有一棵拥有键值、优先级两种权值且满足堆的性质的二叉搜索树
,其结构相当于以随机数据插入的二叉搜索树
,若每个结点的优先级事先给定且互不相等,那么整棵树的形态也唯一确定,与元素插入顺序无关
,每个结点的优先级是随机确定的,因此个操作的时间复杂度也随机地,但插入、删除、查找的期望时间复杂度均为O(logn)
,其左右子树也是Treap。通过使二叉搜索树满足堆的性质来尽量避免出现构造出单支树(时间复杂度退化为O(n))的情况,通过随即优先级打破有序序列的构造序列
- 算法思路
Treap通过随机地优先级和旋转来维护Treap的性质,与平衡二叉树不同的是,Treap只需要两种旋转方式,向左旋转和向右旋转
算法
旋转
void Treap::leftRotate(Node* &p) {
Node *k = p->right;
p->right = k->left;
k->left = p;
p = k;
}
void Treap::rightRotate(Node* &p) {
Node *k = p->left;
p->left = k->right;
k->right = p;
p = k;
}
- 思路
基于结点p,向左旋转时,指针k记录当前结点的右子树,将k的左子树作为结点p新的右子树,将结点p作为k的左子树,最后使p指向结点k,结点p必须是结点指针的引用,以保证结点p的父结点的左右孩子被改变
- 图解
插入
void Treap::insert(Node* &p, int value) {
if (p == NULL) {
p = new Node(value, rand());
} else {
if (value == p->data) {
return;
} else if (value < p->data) {
insert(p->left, value);
} else {
insert(p->right, value);
}
if(p->left && p->left->priority > p->priority) {
rightRotate(p);
} else if(p->right && p->right->priority < p->priority) {
leftRotate(p);
}
}
}
- 思路
插入结点时,递归地,根据二叉搜索树的性质找到结点应该插入的位置
,产生一个随机数作为优先级,将结点插入Treap中,然后判断是否需要旋转
,若当前子树根节点的左子树存在且优先级大于当前子树优先级,则右旋转,若当前子树根节点的右子树存在且优先级大于当前子树优先级,则左旋转,使得Treap维持堆的性质
删除
- 思路
递归地,根据二叉搜索树的性质找到待删除结点的位置
,为维护Treap保持堆的性质(优先级保持堆的性质,关键字保持二叉搜索树的性质)
,分为三种情况
- 待删除结点为叶节点
直接删除 - 待删除结点为叶节点有且仅有一棵子树
将其子树代替待删除结点为叶节点即可 - 待删除结点为叶节点有两棵子树
将两棵子树中优先级级高的旋转到根结点
,然后递归地在另一棵子树中(待删除节点作为原子树根已经旋转到另一个子树)
删除待删除节点
- 图解
结点外的值为优先级
查找
实现代码
- 实现中为了看到效果,在类中增加了一个成员函数用于返回Treap中的最小值
#include<bits/stdc++.h>
using namespace std;
struct Node {
int data;
Node *left;
Node *right;
int priority;
Node(int value, int level) : data(value), left(NULL), right(NULL), priority(level) {}
};
class Treap {
private:
Node *root;
void leftRotate(Node* &p);
void rightRotate(Node* &p);
void insert(Node* &p, int value);
void remove(Node* &p, int value);
public:
Treap();
void insert(int value);
void remove(int x);
bool search(int x);
int smallest();
};
Treap::Treap() {
root = NULL;
}
void Treap::leftRotate(Node* &p) {
Node *k = p->right;
p->right = k->left;
k->left = p;
p = k;
}
void Treap::rightRotate(Node* &p) {
Node *k = p->left;
p->left = k->right;
k->right = p;
p = k;
}
void Treap::insert(int value) {
insert(root, value);
}
void Treap::insert(Node* &p, int value) {
if (p == NULL) {
p = new Node(value, rand());
} else {
if (value == p->data) {
return;
} else if (value < p->data) {
insert(p->left, value);
} else {
insert(p->right, value);
}
if(p->left && p->left->priority > p->priority) {
rightRotate(p);
} else if(p->right && p->right->priority < p->priority) {
leftRotate(p);
}
}
}
void Treap::remove(int value) {
remove(root, value);
}
void Treap::remove(Node* &p, int value) {
if (p->data == value) {
if (p->left == NULL) {
p = p->right;
} else if (p->right == NULL) {
p = p->left;
} else {
if (p->left->priority > p->right->priority) {
rightRotate(p);
remove(p->right, value);
} else if (p->left->priority < p->right->priority) {
leftRotate(p);
remove(p->left, value);
}
}
} else {
if (value < p->data) {
remove(p->left, value);
} else {
remove(p->right, value);
}
}
}
bool Treap::search(int value) {
Node *p = root;
while (p) {
if (p->data == value) {
return true;
} else {
p = p->data < value ? p->right : p->left;
}
}
return false;
}
int Treap::smallest() {
Node* p = root;
int value;
while (p) {
value = p->data;
p = p->left;
}
return value;
}
int main(int argc, char const *argv[]) {
Treap treap;
int N;
scanf("%d", &N);
for (int i = 0; i < N; i++) {
int value;
scanf("%d", &value);
treap.insert(value);
}
for (int i = 0; i < N; i++) {
int value = treap.smallest();
printf("%d ", value);
treap.remove(value);
}
return 0;
}
输入数据
10
20 19 28 34 123 8900 21334 4345 234 567
输出数据
19 20 28 34 123 234 567 4345 8900 21334
鸣谢
《算法竞赛入门经典训练指南》
最后
- 由于博主水平有限,不免有疏漏之处,欢迎读者随时批评指正,以免造成不必要的误解!