Treap 学习笔记

来源:互联网 发布:fm调频发射器软件 编辑:程序博客网 时间:2024/06/05 13:29

Treap

Treap,顾名思义就是 Tree + Heap。这么命名的原因就是它使用了二叉堆的性质来保持二叉树的平衡。
我们知道,一个二叉(小根)堆满足这样的性质:一个节点的两个儿子的值都小于节点本身的值。如果一个二叉查找树满足这样的性质,那么它就被称作 Treap。
Treap 中每个节点有 2 个值,其中一个满足二叉查找树的性质,一个满足大根堆的性质。把满足二叉查找树性质的值称作 w,把满足大根堆性质的值称作 prio。 对于 Treap 来说,当前节点的 w 值大于左儿子,小于右儿子。当前节点的 prio 值小于儿子节点的值。
每个节点的 w 我们无法改变,为了保证 Treap 的平衡性,我们需要让每个节点的 prio 都取一个随机值,这样我们就可以保证这棵树“基本平衡”。


随机数的生成

如果我们采用系统函数生成的随机数,会有出现重复的可能性。如果 prio 取到了重复的值,则必然会造成堆结构的混乱。
生成不重复的随机数的办法:

inline int random_f() {    static int seed = 623; // seed 可以随便取?    return seed = int(seed * 48271LL % 2147483647);}

48271 就可以取遍 1 - 2147483647 中的所有数字。


插入

给节点随机分配一个优先级,先把要插入的点插入到一个叶子上,然后跟维护堆一样,我们维护一个小根堆,如果当前节点的优先级比根大就旋转,如果当前节点是根的左儿子就右旋,如果当前节点是根的右儿子就左旋。

由于旋转是 O(1) 的,最多进行 h 次(h 是树的高度),插入的复杂度是 O(h) 的,在期望情况下 h=O(logn),所以它的期望复杂度是 O(logn)


其实也就是说,
右旋就是,让当前节点降为自己的右儿子,让左儿子代替自己,并让自己左儿子的右儿子成为自己的左儿子。
左旋相同,就是让当前节点降为自己的左儿子,让右儿子代替自己,并让自己右儿子的左儿子成为自己的右儿子。


删除

因为 Treap 满足堆性质,所以只需要把要删除的节点旋转到叶节点上,然后直接删除就可以了。

具体的方法:

如果该节点的左子节点的优先级小于右子节点的优先级,右旋该节点,使该节点降为右子树的根节点,然后访问右子树的根节点,继续操作;
反之,左旋该节点,使该节点降为左子树的根节点,然后访问左子树的根节点,继续操作,直到变成可以直接删除的节点。
(即:让优先级小的结点旋到上面,满足堆的性质)

删除最多进行 O(h) 次旋转,期望复杂度是 O(logn)


Luogu P3369 【模板】普通平衡树(Treap/SBT)

  1. 插入x数
  2. 删除x数(若有多个相同的数,因只删除一个)
  3. 查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
  4. 查询排名为x的数
  5. 求x的前驱(前驱定义为小于x,且最大的数)
  6. 求x的后继(后继定义为大于x,且最小的数)
#include <bits/stdc++.h>using namespace std;const int N = 2e5 + 5; inline int random_f() {    static int seed = 623;    return seed = int(seed * 48271LL % 2147483647);}struct Treap {    int rt, cnt;    int w[N], prio[N], size[N], lson[N], rson[N];    inline void clear() {        rt = 0; cnt = 0;        memset(w, 0, sizeof(w));        memset(prio, 0, sizeof(prio));        memset(size, 0, sizeof(size));        memset(lson, 0, sizeof(lson));        memset(rson, 0, sizeof(rson));    }    inline void pushup(int &p) {size[p] = size[lson[p]] + size[rson[p]] + 1;}    inline void right_rorate(int &p) {        int tmp = lson[p];        lson[p] = rson[tmp];        rson[tmp] = p;        size[tmp] = size[p];        pushup(p);        p = tmp;    }    inline void left_rorate(int &p) {        int tmp = rson[p];        rson[p] = lson[tmp];        lson[tmp] = p;        size[tmp] = size[p];        pushup(p);        p = tmp;    }    inline void insert(int &p, int x) {        if(p == 0) {            p = ++ cnt;            size[p] = 1, w[p] = x, prio[p] = random_f();            return ;        }        size[p] ++;        insert((x >= w[p]) ? rson[p] : lson[p], x);        if(prio[lson[p]] < prio[p] && lson[p] != 0) right_rorate(p);        if(prio[rson[p]] < prio[p] && rson[p] != 0) left_rorate(p);        pushup(p);    }    inline void delete_f(int &p, int x) {        size[p] --;        if(w[p] == x) {            if(lson[p] == 0 && rson[p] == 0) {p = 0; return ;}            if(lson[p] == 0 || rson[p] == 0) {p = lson[p] + rson[p]; return ;}            if(prio[lson[p]] < prio[rson[p]]) {                right_rorate(p);                delete_f(rson[p], x);                return ;            } else {                left_rorate(p);                delete_f(lson[p], x);                return ;            }        }        delete_f((x >= w[p]) ? rson[p] : lson[p], x);        pushup(p);    }       inline int rank(int &p, int x) {        if(p == 0) return 0;        int tmp;        if(x > w[p]) tmp = size[lson[p]] + 1 + rank(rson[p], x);        else tmp = rank(lson[p], x);        return tmp;    }    inline int select(int &p, int x) {        if(x == size[lson[p]] + 1) return w[p];         if(x > size[lson[p]] + 1) return select(rson[p], x - size[lson[p]] - 1);        else return select(lson[p], x);    }    inline int querypre(int &p, int x) {        if(p == 0) return 0; // 找不到超过 x 的,就返回 0,tmp 的 0 是在这里赋值的         if(w[p] >= x) return querypre(lson[p], x);        // 此时 w[p] < x,再到 p 的右子树里找一个最大的不超过 x 的,即为答案        int tmp = querypre(rson[p], x);        return (tmp == 0) ? w[p] : tmp;    }    inline int querysuf(int &p, int x) {        if(p == 0) return 0;        if(w[p] <= x) return querysuf(rson[p], x);        int tmp = querysuf(lson[p], x);        return (tmp == 0) ? w[p] : tmp;    }}treap;int main() {    int n;    scanf("%d", &n);    treap.clear();    for(int i = 1; i <= n; i ++) {        int opt, x;        scanf("%d%d", &opt, &x);        if(opt == 1) treap.insert(treap.rt, x);        if(opt == 2) treap.delete_f(treap.rt, x);        if(opt == 3) printf("%d\n", treap.rank(treap.rt, x) + 1);        if(opt == 4) printf("%d\n", treap.select(treap.rt, x));        if(opt == 5) printf("%d\n", treap.querypre(treap.rt, x));        if(opt == 6) printf("%d\n", treap.querysuf(treap.rt, x));    }    return 0;}
原创粉丝点击