旋转Treap
Treap树的名字来自于Tree+Heap,因为它结合了树(二叉搜索树)和堆(二叉堆)。(所以可以翻译为树堆?)。
虽然感觉除了代码长点,这个旋转Treap也不是不好,但是fhp能做到的它做不到……不过一些操作下反而更快
虽然Treap相对简单,但是下面这行代码对写平衡树很有用。
#define debug(x) std::cerr << #x << " " << x << '\n'
结点性质
在Treap中,各个结点的值val
具有跟二叉搜索树一致的性质。同时,每个Treap的结点中还有一个权值(优先值)rank
,整颗Treap的结点权值分布类似于大根堆或者小根堆。
也就是因为这个rank
是随机数,所以其分配到哪个值都有可能。因此Treap的整个结构是期望平衡的。
因此,在旋转Theap中需要维护上面的两个性质,因此其具有一个关键的操作:旋转。
Treap的结点定义如下。
struct Node {
int val, rank, cnt, size;
int child[2];
};
旋转
Treap的旋转就像二叉树的旋转一样,具有左旋和右旋。(这个可以画个图去理解。)
- 左旋,即将当前节点的左儿子提上来,同时这个结点变成其左儿子的右节点。
- 右旋,即将当前节点的右儿子提上来,同时这个结点变为其右儿子的左节点。
旋转操作可以有两种实现,根据孩子结点的实现的不同,有具体实现左旋和右旋,也有像下面方式的一个函数实现旋转(根据参数d旋转)。
class Treap {
private:
struct Node {
int val, size, cnt, rank;
int child[2];
Node(int val = 0, int size = 0, int cnt = 0, int rank = 0)
: val(val), size(size), cnt(cnt), rank(rank) {
child[0] = child[1] = 0;
}
bool operator < (const Node &a) const {
return rank < a.rank;
}
bool operator > (const Node &a) const {
return rank > a.rank;
}
};
vector<Node>tr;
int tot, root;
int create(int val) {
int now = ++ tot;
tr[now].size = tr[now].cnt = 1;
tr[now].val = val;
tr[now].rank = rand();
return now;
}
void update(int now) {
tr[now].size = tr[now].cnt + tr[tr[now].child[0]].size + tr[tr[now].child[1]].size;
}
void rotate(int &now, int d) {
int tmp = tr[now].child[d ^ 1];
tr[now].child[d ^ 1] = tr[tmp].child[d];
tr[tmp].child[d] = now;
update(now);
update(tmp);
now = tmp;
}
public:
void inti(int size) {
tr.assign(size + 1, Node());
tot = root = 0;
}
void insert(int &now, int val);
void insert(int val) {
insert(root, val);
}
void delet(int &now, int val);
void delet(int val) {
delet(root, val);
}
};
插入
Treap的插入和BST的插入一致。但是要注意维护优先值的堆的性质。
void Treap::insert(int &now, int val) {
if (!now) {
now = create(val);
return;
}
if (tr[now].val == val) {
tr[now].cnt ++;
} else {
int d = val < tr[now].val ? 0 : 1;
insert(tr[now].child[d], val);
if (tr[now] < tr[tr[now].child[d]]) {
rotate(now, d ^ 1);
}
}
update(now);
}
删除
Treap的删除可以采用BST的策略删除,也可以使用堆的性质去删除。
void Treap::delet(int &now, int val) {
if (!now) {
return;
}
if (tr[now]->val == val) {
if (tr[now]->cnt > 1) {
tr[now]-