用Treap实现名次树

这篇博客详细介绍了如何使用Treap数据结构来实现名次树,包括Treap的基本概念、节点定义、旋转操作、插入和删除节点的方法,以及查找功能。此外,还针对LA5031算法竞赛题目提供了解题思路和AC代码,是算法竞赛入门的经典训练指南之一。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

来自《算法竞赛入门经典训练指南》

1.Treap实现名次树

1.简单介绍

Treap是一棵拥有键值和优先级两种权值的树。对于键值而言,这棵树是二叉排序树。对于优先级而言,这棵树是堆,即在这棵树的任意子树中,根的优先级是最大的。
不难证明,如果每个结点的优先级事先给定且互不相等,整棵树的形态也就唯一确定了,和元素的插入顺序无关。在Treap的插入算法中,每个节点的优先级是随机确定的。
因此各个操作的时间复杂度也是随机的。幸运的是,可以证明插入、删除和查找的期望时间复杂度均为O(nlogn)。

2.Treap结点的定义

struct Node
{
    Node *ch[2];//左右子树
    int r;//优先级,数值越大,优先级越高
    int v;//值
    int s;//结点总数
    int cmp(int x) const{
        if(x==v) return -1;
        return x<v?0:1;
    }
};

3.旋转

分为左旋和右旋
//d=0表示左旋,d=1表示右旋
void rotate(Node* &o,int d)
{
    Node* k=o->ch[d^1];
    o->ch[d^1]=k->ch[d];
    k->ch[d]=o;
    o=k;
}
注意小技巧:因为d=0或者1,d^1定价于1-d,但计算速度更快。关于左旋或者右旋,可自行百度……

4.插入结点

插入结点时,先随机给新结点一个优先级,然后执行普通的插入算法(根据键值大小判断插入哪颗子树中。执行完毕后,用左右旋,维护Treap树的堆性质。
如要采用递归实现,则只需在递归插入之后判断是否需要旋转。
//在以o为根的子树中插入键值x,修改o
void insert(Node* &o,int x)
{
    if(o==NULL){
        o=new Node();
        o->ch[0]=o->ch[1]=NULL;
        o->v=x;
        o->r=rand();
    }
    else{
        int d=o->cmp(x);
        insert(o->ch[d],x);
        if(o->ch[d]->r>o->r){
            rotate(o,d^1);
        }
    }
}

5.删除结点

删除结点,如果只有一颗子树,直接用这棵子树代替待删除结点成为根即可(注意o是叶子的情况也符合在内)。麻烦的是o有两棵子树的情况,我们先把优先级较高的一颗旋转到根,然后递归在另一棵子树中删除结点o。例如,左子结点的优先级较高,就必须进行右旋,否则会违反堆性质。
void remove(Node* &o,int x)
{
    int d=o->cmp(x);
    if(d==-1){
        if(o->
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值