前面写了BST。而Treap是一种简单好写的动态平衡BST,。
Treap = Tree + heap,树堆
Treap是一个有值(value)优先级(priority)的树。看value,Treap是BST;看priority,Treap是堆。这里写的是大根堆,即每个结点的优先级大于它的孩子.
下面是一些操作。
#旋转:有点难理解,下面是我的总结的旋转过程
当前结点要旋转的结点为k,左孩子是k.l,右孩子是k.r
左旋:右子树换成k.r的左子树,k父亲改成k.r
右旋:左子树换成k.l的右子树,k父亲改成k.l
左旋右旋完全相反,互为逆操作。
上个图,不然容易晕眩而且看不懂的
#插入:先随机赋给新节点一个priority,然后用BST的插入方法插入。然后再往上走,根据优先级维护堆的性质,不满足就旋转
生成随机数?当然是rand()函数;
#删除:理解插入以后,删除也不难,过程相反。
先找到结点,如果只有一棵子树,直接把子树结点接到父亲上。如果有两棵,把优先级高的旋上来,再递归删除这个结点.
同样封装到struct Treap里了。
struct Treap {
struct Node {
Node * ch[2];
int priority, value;
int cmp(int x) {
if(x == value) return -1;
return x > value;
}
};
void rotate(Node* &o, int d) { //d=0左旋
Node *k = o->ch[d^1]; //d^1相当于1-d
o->ch[d^1] = k->ch[d];
k->ch[d] = o;
o = k;
}
void insert(Node* &o, int x) {
if(o == NULL) {
o = new Node();
o -> ch[0] = o -> ch[1] = NULL;
o -> value = x;
o -> priority = rand();
} else {
int d = o->cmp(x);
insert(o->ch[d], x);
if(o->ch[d] -> priority > o->priority)
rotate(o, d^1);
}
}
void remove(Node* &o, int x) {
int d = o->cmp(x);
if(d == -1) {
if(o->ch[0] == NULL) o = o->ch[1];
else if(o->ch[1] == NULL) o = o->ch[0];
else {
int d2 = (o->ch[0]->priority > o->ch[1]->priority);
rotate(o, d2);
remove(o->ch[d2], x);
}
}
}
bool find(Node* &o, int x) {
while(o != NULL) {
int d = o -> cmp(x);
if(d == -1) return true;
o = o->ch[d];
}
return false;
}
};
Treap的功能还有-名次树(Rank Tree)。
每个结点多一个sz值,表示以它为根的树结点数.
#Rank x: 查找值为x的名次
rk初始化为左子树大小
if x在左子树 在左子树查找
if x为当前结点 答案为rk+1
else 返回rk+ 当前结点cnt + 右子树查找rank
#Kth k:查找名字为k的值
sz初始化为左子树大小
if k<=sz 在左子树查找
else if k在sz+1和sz+cnt之间,说明找的是此结点,返回当前结点value
else 返回右子树找k-sz-cnt名.
这两个都不难,很容易实现
之前没有介绍,Treap每个结点不允许有value重复,因此每个结点再加一个值叫cnt(初始化=1),记录出现次数.
看下Treap洛谷原题
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
struct Treap_Rank_Tree {
struct Node {
Node * ch[2];
int priority, value, cnt, sz;
Node(int v) : value(v) {sz = 1; cnt = 1; priority = rand(); ch[0] = ch[1] = NULL;}
int cmp(int x) {
if(x == value) return -1;
return x > value;
}
void maintain() {
sz = cnt;
if(ch[0] != NULL) sz += ch[0]->sz;
if(ch[1] != NULL) sz += ch[1]->sz;
}
};
Node *Root;
Treap_Rank_Tree() {
Root = NULL;
}
void rotate(Node* &o, int d) {
Node *k = o->ch[d^1];
o->ch[d^1] = k->ch[d];
k->ch[d] = o;
o->maintain();
k->maintain();
o = k;
}
void insert(Node* &o, int x) {
if(o == NULL) o = new Node(x);
else {
int d = o->cmp(x);
if(d == -1) {
o->cnt ++;
o->sz ++;
return;
}
insert(o->ch[d], x);
if(o->ch[d] -> priority > o->priority)
rotate(o, d^1);
o -> maintain();
}
}
void remove(Node* &o, int x) {
if(o == NULL) return;
int d = o->cmp(x);
if(d == -1) {
if(o->cnt > 1) {
o->cnt --;
o->sz --;
return;
}
if(o->ch[0] == NULL) o = o->ch[1];
else if(o->ch[1] == NULL) o = o->ch[0];
else {
int d2 = (o->ch[0]->priority < o->ch[1]->priority);
rotate(o, d2);
remove(o->ch[d2], x);
}
} else remove(o->ch[d], x);
if(o != NULL) o -> maintain();
}
bool find(Node* &o, int x) {
while(o != NULL) {
int d = o -> cmp(x);
if(d == -1) return true;
o = o->ch[d];
}
return false;
}
int Rank(Node* o, int x) {
int rk = 0;
if(o->ch[0] != NULL) rk = o->ch[0]->sz;
if(x < o->value) return Rank(o->ch[0], x);
if(x == o->value) return rk+1;
return rk + o->cnt + Rank(o->ch[1], x);
}
int Kth(Node* o, int k) {
int sz = 0;
if(o->ch[0] != NULL) sz = o->ch[0]->sz;
if(k <= sz) return Kth(o->ch[0], k);
else if(k <= sz + o->cnt) return o->value;
return Kth(o->ch[1], k-sz-(o->cnt));
}
int _Find_1(Node* o, int x) { //找前驱
if(o == NULL) return -2e9;
if(o->value < x) return max(o->value, _Find_1(o->ch[1], x));
else return _Find_1(o->ch[0], x);
}
int _Find_2(Node* o, int x) { //找后继
if(o == NULL) return 2e9;
if(o->value > x) return min(o->value, _Find_2(o->ch[0], x));
else return _Find_2(o->ch[1], x);
}
} treap;
int main() {
srand(time(NULL));
int Q, opt, x;
cin >> Q;
for(int i=1; i<=Q; i++) {
cin >> opt >> x;
if(opt == 1) treap.insert(treap.Root, x);
else if(opt == 2) treap.remove(treap.Root, x);
else if(opt == 3) cout << treap.Rank(treap.Root, x) << endl;
else if(opt == 4) cout << treap.Kth(treap.Root, x) << endl;
else if(opt == 5) cout << treap._Find_1(treap.Root, x) << endl;
else if(opt == 6) cout << treap._Find_2(treap.Root, x) << endl;
}
return 0;
}