关于平衡树(Treap)

平衡树是什么?

其实平衡树就是支持旋转二叉查找树


什么是二叉查找树呢?

其实就是满足(左子树(全部节点) < 根 < 右子树(全部节点))的一棵树,比如↓

(注意并不是每个节点都要是满的,也就是说它不一定是一棵完全二叉树)

那么二叉查找树有什么用呢?

假如我们要查询第k大的数,我们从根节点开始往下走,假如左子树大小大于等于k,我们就进入左子树去找;如果左子树大小等于(k-1),就输出当前节点;假如左子树大小小于(k-1),我们就进入右子树找排名为(k-(左子树大小+1))的数。

这样由于树的理想高度为logn,所以单次查找复杂度为O(logn).

同理,插入、删除、查询前驱后继等操作的复杂度都为O(logn).

可是,二叉查找树并不能处理特殊情况,比如一个递增数列,那么如果你一个一个插入的话二叉查找树就退化成了一条,不具备任何优越性了。

所以有前辈发明了Treap.



Treap是一个同时具有二叉查找树与堆的性质的树形结构(堆的本质也是二叉树)。比如↓

具体的实现过程就是我们在新加入一个节点时赋给他本身键值的同时给他一个优先级,而这个优先级随机产生,那就不怕被特殊数据卡了对不对!

那我们怎么维护堆的性质呢?

——旋转。

假如左图中x的优先级大于u,那就要通过右旋把x旋转到根的位置,同时为了维护二叉查找树的性质,所以要把x的右儿子接到u上。

(自己举个例子画画图就明白了)。

旋转代码:

inline void rotate(Node* &cur,int d) {
    Node *k = cur->ro[d^1] ;
    cur->ro[d^1] = k->ro[d] ;
    k->ro[d] = cur ;
    cur->pushup() ; k->pushup() ;
    cur = k ;
}

(ro[0],ro[1]分别代表左右儿子,当d等于0时表示左旋,等于1时表示右旋)



给出模板题与参考代码:(我建的是大根堆)

https://www.luogu.org/problemnew/show/P3369

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int INF = (1<<29) ;

inline int read() {
    int k = 0 , f = 1 ; char c = getchar() ;
    for( ; !isdigit(c) ; c = getchar())
      if(c == ‘-‘) f = -1 ;
    for( ; isdigit(c) ; c = getchar())
      k = k*10 + c-‘0‘ ;
    return k*f ;
}

inline int randomm() {
    static int seed = 707 ;
    return seed = seed*3214567%23456789 ;
}

struct Node {
    int key, siz, rap ;
    Node *ro[2] ;
    Node(int x) {
        siz = 1 ;
        key = x ;
        rap = randomm() ;
        ro[1] = ro[0] = NULL ;
    }
    inline int pushup() {
        siz = 1 ;
        if(ro[0]) siz += ro[0]->siz ;
        if(ro[1]) siz += ro[1]->siz ;
    }
    inline int cop(int x) {
        if(x == key) return -1 ;
        return x < key ? 0 : 1 ;
    }
};

int n, x, k, accm, ans ; 

inline void rotate(Node* &cur,int d) {
    Node *k = cur->ro[d^1] ;
    cur->ro[d^1] = k->ro[d] ;
    k->ro[d] = cur ;
    cur->pushup() ; k->pushup() ;
    cur = k ;
}

void insert(Node* &cur) {
    if(!cur) {
        cur = new Node(x) ; return ;
    }
    int d = x < cur->key ? 0 : 1 ;
    insert(cur->ro[d]) ;
    if(cur->ro[d]->rap > cur->rap) rotate(cur,d^1) ;
    else cur->pushup() ;
}

void Delete(Node* &cur) {
    int d = cur->cop(x) ;
    if(d == -1) {
        if(!cur->ro[0]) cur = cur->ro[1] ;
        else if(!cur->ro[1]) cur = cur->ro[0] ;
        else {
            int dd = cur->ro[0]->rap < cur->ro[1]->rap ? 0 : 1 ;
            rotate(cur,dd) ; Delete(cur->ro[dd]) ;
        }
    } else Delete(cur->ro[d]) ;
    if(cur) cur->pushup() ;
}

void rank(Node *cur) {
    if(!cur) return ;
    if(x < cur->key) {
        rank(cur->ro[0]) ; return ;
    }
    int lsiz = cur->ro[0] == NULL ? 0 : cur->ro[0]->siz ;
    if(x == cur->key) {
        ans = min(ans,accm+lsiz+1) ; rank(cur->ro[0]) ;
    } else {
        accm += (lsiz+1) ; rank(cur->ro[1]) ;
    }
}

void kth(Node *cur) {
    int cm = 1 ;
    if(cur->ro[0]) cm += cur->ro[0]->siz ;
    if(cm == k) {
        ans = cur->key ;
    } else if(k < cm) {
        kth(cur->ro[0]) ;
    } else {
        k -= cm ; kth(cur->ro[1]) ;
    }
}

void pre(Node *cur) {
    if(!cur) return ;
    if(cur->key < x) {
        ans = max(ans,cur->key) ; pre(cur->ro[1]) ;
    } else pre(cur->ro[0]) ;
}

void sub(Node *cur) {
    if(!cur) return ;
    if(cur->key > x) {
        ans = min(ans,cur->key) ; sub(cur->ro[0]) ;
    } else sub(cur->ro[1]) ;
}

int main() {
    n = read() ;
    Node *root = NULL ;
    int opt ;
    while(n--) {
        opt = read() ;
        switch(opt) {
            case 1:x = read(), insert(root) ; break ;
            case 2:x = read(), Delete(root) ; break ;
            case 3:x = read(), accm = 0, ans = INF, rank(root), printf("%d\n",ans) ; break ;
            case 4:k = read(), kth(root), printf("%d\n",ans) ; break ;
            case 5:x = read(), ans = -INF, pre(root), printf("%d\n",ans) ; break ;
            case 6:x = read(), ans = INF, sub(root), printf("%d\n",ans) ; break ;
        }
    }
    return 0 ;
}


再给出一道基础应用题:

https://www.luogu.org/problemnew/show/P1503

(未完待续)

原文地址:https://www.cnblogs.com/zub23333/p/8449074.html

时间: 2024-10-11 09:43:44

关于平衡树(Treap)的相关文章

hiho 1325 : 平衡树&#183;Treap

#1325 : 平衡树·Treap 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho:小Hi,我发现我们以前讲过的两个数据结构特别相似. 小Hi:你说的是哪两个啊? 小Ho:就是二叉排序树和堆啊,你看这两种数据结构都是构造了一个二叉树,一个节点有一个父亲和两个儿子. 如果用1..n的数组来存储的话,对于二叉树上的一个编号为k的节点,其父亲节点刚好是k/2.并且它的两个儿子节点分别为k*2和k*2+1,计算起来非常方便呢. 小Hi:没错,但是小Hi你知道有一种办

算法模板——平衡树Treap 2

实现功能:同平衡树Treap 1(BZOJ3224 / tyvj1728) 这次的模板有了不少的改进,显然更加美观了,几乎每个部分都有了不少简化,尤其是删除部分,这个参照了hzwer神犇的写法,在此鸣谢,然后,贴模板走人 1 var 2 i,j,k,l,m,n,head,tot:longint; 3 a,b,lef,rig,fix:array[0..100010] of longint; 4 function min(x,y:longint):longint; 5 begin 6 if x<y

luoguP3369[模板]普通平衡树(Treap/SBT) 题解

链接一下题目:luoguP3369[模板]普通平衡树(Treap/SBT) #include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #include<cstring> #include<iomanip> #include<algorithm> #include<ctime> #include<queue> #incl

平衡树 Treap

//平衡树 Treap //维护一个堆使得随机权值小(大)的数始终在上方 //使用随机权值目的:防止出题人卡 #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; struct uio{ int l,r,siz,num,rd,tim;

算法模板——平衡树Treap

实现功能如下——1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数,因输出最小的排名)4. 查询排名为x的数5. 求x的前驱(前驱定义为小于x,且最大的数)6. 求x的后继(后继定义为大于x,且最小的数) 本程序的实现原理为Treap平衡树 详见BZOJ3224 1 var 2 i,j,k,l,m,n,head,ts:longint;f1:text; 3 a,b,fix,lef,rig:array[0..500000] of longint; 4

BZOJ3224: Tyvj 1728 普通平衡树[treap]

3224: Tyvj 1728 普通平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 9046  Solved: 3840[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数,因输出最小的排名)4. 查询排名为x的数5. 求x的前驱(前驱定义为小

各种平衡树Treap/SBT/Avl/Splay tree

看了这么久的平衡树,是时候做个总结了. 以poj 3481为例,敲了四份代码,分别是Treap ,Size Balance Tree,Avl Tree,splay tree. 唯独少了红黑树T_T... 总的来说每种平衡树各有各的优点吧: Treap写起来简单上手也快如果熟练的话不到十分种可以敲完. SBT速度快名不虚传... Avl树高度平衡吧,不过实际的效果不尽如人意,可能是我实现的姿势不对吧/(ㄒoㄒ)/~~ splay tree各方面比较均衡,特别的伸展树在维护序列方面相对其它几种树优势

【bzoj3224】普通平衡树——treap

我的第一道treap题目,treap的模版题. 代码是对着hzw的敲的,一边敲一边理解... 主要是熟悉一下treap的各种基本操作,详细细节看代码. #include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> using namespace std; struct point { int l,r,v,rnd,size,w; }tree[100005]; int n,size

BZOJ 3224: Tyvj 1728 普通平衡树 treap

3224: Tyvj 1728 普通平衡树 Description 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数,因输出最小的排名)4. 查询排名为x的数5. 求x的前驱(前驱定义为小于x,且最大的数)6. 求x的后继(后继定义为大于x,且最小的数) Input 第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

BZOJ_3224_普通平衡树(Treap)

描述 Treap模板题.支持如下几种操作: 1.插入; 2.删除; 3.rank(x); 4.kth(k); 5.pre(x); 6.suc(x); 分析 写多了就熟了...昨天看了书,今天调试的时候敲了好多遍,感觉满脑子是tree+heap. p.s. 1.rank,kth,pre,suc函数都可以写成非递归的形式. 2.remove函数有两种写法: (1).LRJ白书上的方法: 如果待删除节点只有一个子树,则用该子树代替待删除节点,删除待删除节点即可.如果有两个子树,则将优先值小的(小根堆)