码迷,mamicode.com
首页 > 其他好文 > 详细

平衡树总结

时间:2020-02-03 17:23:30      阅读:88      评论:0      收藏:0      [点我收藏+]

标签:生成   turn   避免   查询   upload   原理   heap   节点   记录   

众所周知, BST 的操作复杂度是 \(O(\log n)\) ,但 BST 可能会退化成一条链,于是我们可以使用平衡树。

平衡树有很多种,但我还只会三种(我太菜了):Treap,Splay,fhq-Treap

Treap

Treap 记录了一个优先级,并在满足构成 BST 的同时,会按照优先级的大小来满足堆的性质,于是有了 Treap (Tree + Heap) 的名字,也防止了它退化成一条链。

左旋(zig)与右旋(zag)

通过 zig 和 zag 来维护堆的性质。

技术图片

技术图片

void pushup(int o) { sz[o] = sz[ls[o]] + sz[rs[o]] + cnt[o]; }

void zig(int &o) {
    int p = rs[o];
    rs[o] = ls[p], ls[p] = o, sz[p] = sz[o];
    pushup(o), o = p;//是pushup原先的o结点,sz[p]已将更新过了
}

void zag(int &o) {
    int p = ls[o];
    ls[o] = rs[p], rs[p] = o, sz[p] = sz[o];
    pushup(o), o = p;
}

插入

其它的操作实际上和 BST 是一样的,只是注意要维护一下优先级,左旋或者右旋就好了。

void insert(int &o, int k) {
    if (!o) {
        o = ++tot, sz[o] = cnt[o] = 1, val[o] = k, rd[o] = rand();
        return;
    }
    sz[o]++;
    if (val[o] == k) cnt[o]++;
    else if (k < val[o]) {
        insert(ls[o], k);
        if (rd[ls[o]] < rd[o]) zag(o);
    }
    else {
        insert(rs[o], k);
        if (rd[rs[o]] < rd[o]) zig(o);
    }
}

查询排名

int GetRank(int o, int k) {
    if (!o) return 0;
    if (k < val[o]) return GetRank(ls[o], k);
    else if (k > val[o]) return GetRank(rs[o], k) + cnt[o] + sz[ls[o]];
    else return sz[ls[o]] + 1;
}

查询值

int GetVal(int o, int k) {
    if (!o) return 0;
    if (k <= sz[ls[o]]) return GetVal(ls[o], k);
    else if (k > sz[ls[o]] + cnt[o]) return GetVal(rs[o], k - sz[ls[o]] - cnt[o]);
    else return val[o];
}

前驱

int suf(int o, int k) {
    if (!o) return INF;
    if (k >= val[o]) return suf(rs[o], k);
    else return min(val[o], suf(ls[o], k));
}

后继

int suf(int o, int k) {
    if (!o) return INF;
    if (k >= val[o]) return suf(rs[o], k);
    else return min(val[o], suf(ls[o], k));
}

模板题代码:Code

vector 版本(手动狗头):Code

Splay

与 Treap 不同,Splay 通过不断将一个数旋转到根来保持平衡。

至于为什么可以......

以下出自大佬的 blog

Splay 为什么能让 BST 保持平衡玄学原理很多博客未提及。自己 yy 了一天,搞出了个理由,表述不严谨,意会一下。粗略证明:对于随机生成的数据,裸 BST 本来就可以平衡,而 Splay 这种旋转行为的本身对于数据也是随机性的,所以最后还是可以平衡;对于毒瘤单调递增或递减的数据,裸 BST 不能平衡,效率低的原因可以看做是因为树退化成链,也可以看做是因为每一个新节点在插入时都需要比较一些严重脱离当前插入数据范围(趋势)的数据(如插入 1,2,3……10,1000,1001,1002……1010 时,每次插入大于等于 1000 的数时,裸 BST 每次都要先和前 10 个比较大小,但是其实这是不必要的,因为前 10 个数远小于插入的数,如果像这样每次都要访问这些低频节点,会大大增加其复杂度),而每次的 Splay 操作就是使根节点尽量符合当前插入数据的趋势,避免冗余的比较,让那些低频节点访问次数降低。证毕

先咕了

调板子调到怀疑人生......

Code

平衡树总结

标签:生成   turn   避免   查询   upload   原理   heap   节点   记录   

原文地址:https://www.cnblogs.com/hlw1/p/12256417.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!