描述
Treap=Tree+Heap(堆树)。
Treap
- 是一棵二叉搜索树
- 树的每个节点存储左指针和右指针,以及一个优先级,这个优先级是建树的时候随机制定的。
- 树的节点优先级满足堆序性质:任何节点的优先级必须至少和它的父节点的优先级一样大。
- 根节点的优先级最低。
这样的话,Treap是有一个随机附加域满足堆的性质的二叉搜索树,其结构相当于以随机数据插入的二叉搜索树。
Treap ADT
#define INT_MAX SOMEMAXNUM
template <class T>
struct TreapNode
{
T elem;
TreapNode *left;
TreapNode *right;
int priority;
TreapNode():left{nullptr},right{nullptr},priority{INT_MAX}{};
TreapNode(const T &e,TreapNode *lt,TreapNode *rt, int pr):elem{e},left{lt},right{rt},priority{pr}{};
};
template <class T>
class Treap{
private:
TreapNode *node;
TreapNode *nullNode;
public:
Treap(){
nullNode = new TreapNode;
nullNode->left = nullNode->right = nullNode;
nullNode->priority = INT_MAX; //标记空节点的优先级为无穷大
root = nullNode;
}
TreapNode(const TreapNode & rhs);
TreapNode(TreapNode && rhs);
~TreapNode();
TreapNode &operator=(const TreapNode &rhs);
TreapNode &operator=(TreapNode &&rhs);
}
复制代码
插入操作
- 先按照BST进行插入
- 然后沿着Treap树向上旋转,知道优先级满足堆序为止
- 旋转的期望次数小于2
- 递归版实现(非递归更优秀)
void insert(const T &x, TreapNode * &t){
if(t == nullNode)
t = new TreapNode(x,nullNode,nullNode,randomNums.nextInt());
else if(x < t->elem){
insert(x,t->left);
if(t->left->priority < t->priority)
rotateWithLeftChild(t);
}
else if(t->elem < x){
insert(x , t->right);
if(t->right->priority < t->priority)
rotateWithRightChild(t);
}
//否则是重复元什么都不做
}
复制代码
删除操作
- 找到相应的结点;
- 若该结点为叶子结点,则直接删除;
- 若该结点为只包含一个叶子结点的结点,则将其叶子结点赋值给它;
- 若该结点为其他情况下的节点,则进行相应的旋转,具体的方法就是每次找到优先级最小的儿子,向与其相反的方向旋转,直到该结点为上述情况之一,然后进行删除。
- 注意的是当节点t逻辑上是树叶时它仍然有nullnode作为左右儿子,让他与右儿子旋转,旋转后t变为nullnode,同左坐儿子可以释放。
- 这个操作是基于没有重复元才成立
void remove(const T&x ,TreapNode *&t){
if(t!= nullNode){
if(x < t->elem)
remove(x, t->left);
else if(t->elem < x)
remove(x, t->right);
else{
//找到了,沿着低优先级路径旋转
if(t->left->priority < t->right->priority)
rotateWithLeftChild(t);
else
rotateWithRightChild(t);
if(t!= nullNode)
remove(x, t);
else{
delete t->left;
t->left = nullNode;
}
}
}
}
复制代码
性能分析
插入与删除的时间复杂度都与树高有关,平均情况下都是O(log n)
Treap树基本操作代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
struct Node
{
int key;
int priority;
Node *left;
Node *right;
};
Node *root; //根节点
void zig(Node * &node){ //右旋
Node *x = node->left;
node->left = x->right;
x->right = node;
node = x;
}
void zag(Node* &node){ //左旋
Node *x = node->right;
node->right = x->left;
x->left = node;
node = x;
}
void t_insert(Node* &root, int key, int priority){
if(root == NULL){
Node *node = new Node;
node->key = key;
node->priority = priority;
node->left = node->right = NULL;
root = node;
}
else if(key < root->key){
t_insert(root->left, key, priority);
if(root->left->priority < root->priority)
zig(root); //右旋
}
else{ //此处没有处理key相同的情况
t_insert(root->right, key, priority);
if(root->right->priority < root->priority)
zag(root); //左旋
}
}
void t_delete(Node *&root, int key){
if(root !=NULL){
if(key < root->key)
t_delete(root->left, key);
else if(root->key < key)
t_delete(root->right, key);
else{ //找到节点
if(root->left == NULL)
root = root->right; //只有右孩子,直接把右孩子替换就可删除
else if(root->right == NULL)
root = root->left;
else{
if(root->left->priority < root->right->priority)
zig(root);
else
zag(root);
if(root != NULL)
t_delete(root,key);
else{
delete root->left;
root->left = NULL;
}
}
}
}
}
void in_order_traverse(Node *root){
if(root != NULL){
in_order_traverse(root->left);
printf("%d ", root->key);
in_order_traverse(root->right);
}
}
int main(){
srand(time(0));
int arr[] = {0,2,1,8,7,9,6,4,3,5};
for(int i=0;i< 10;i++){
int pri = rand();
printf("K:%d Pr:%d\n",arr[i],pri );
t_insert(root,arr[i],pri);
}
in_order_traverse(root);
printf("\n");
t_delete(root,4);
t_delete(root,8);
in_order_traverse(root);
printf("\n");
}
复制代码