今天一天怼了平衡树。深深地被她的魅力折服了。我算是领略到了高级数据结构的美妙。oi太神奇了。

今天初识平衡树,选择了Treap。

Treap又叫树堆,是一个二叉搜索树。我们知道,它的节点插入是随机的,这样大多数情况下一个平衡的树。但是存在极其特殊的情况,就是树退化成链,这样无法支持我们期望的O(log)效率了。所以我们需要给树附加一个随机的域(里面可以理解为优先级),使树构成二叉排序树的同时,还要满足堆的性质。

Treap树建立具体解释:

我们可以使节点优先级大于儿子节点。可以让节点随机域的数值是以它为根的子树里面最小的,左右儿子都比它大,这样是不是就满足堆的性质了?hhh

至于我们要求的本身的数值,要满足二叉排序树的性质,左子树都比它小,右子树都比它大。

下面我来介绍Treap的具体操作和代码

最重要的是旋转操作,其他操作我会在代码中解释。

因为要维护堆的性质,所以要旋转。

因为要维护堆的性质,要把x旋转到node的位置。这时要满足二叉排序树的性质。Node>P>X.旋转后我们依然要满足这个性质。所以我们要让P为node的左儿子,node为X的右儿子。

代码详尽解释:

支持以下操作:

1.插入x数

2. 删除x数(若有多个相同的数,因只删除一个)

3. 查询x数的排名(若有多个相同的数,因输出最小的排名)

4. 查询排名为x的数

5. 求x的前驱(前驱定义为小于x,且最大的数)

6. 求x的后继(后继定义为大于x,且最小的数)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
#define pos(i,a,b) for(int i=(a);i<=(b);i++)
#define pos2(i,a,b) for(int i=(a);i>=(b);i--)
#define N 500010
struct Treap
{
       int l,r,w,v,size,rnd;
       //v为实际数值;rnd为优先级;size为以它为根的子树大小;w为自身节点存的数的个数(数据可以有多个重复的数)
}tree[N];
int n;
int root,size;
void update(int k)
{
     tree[k].size=tree[tree[k].l].size+tree[tree[k].r].size+tree[k].w;
     //更新子树大小
}
void rturn(int &k)
{
     int t=tree[k].l;
     tree[k].l=tree[t].r;
     tree[t].r=k;
     tree[t].size=tree[k].size;
     update(k);
     k=t;
}//右旋转
void lturn(int &k)
{
     int t=tree[k].r;
     tree[k].r=tree[t].l;
     tree[t].l=k;
     tree[t].size=tree[k].size;
     update(k);
     k=t;
}//左旋转
void insert(int &k,int x)
{
    if(k==0)
    {
       size++;
       k=size;
       tree[k].w=tree[k].size=1;
       tree[k].v=x;
       tree[k].rnd=rand();//随机数
       return;
    }
    tree[k].size++;
    if(tree[k].v==x)//如果有多个,w++
       tree[k].w++;
    else
    {
        if(tree[k].v<x)//满足二叉排序树性质
        {
           insert(tree[k].r,x);
           if(tree[tree[k].r].rnd<tree[k].rnd)
              lturn(k);//维护堆性质,左旋转
        }
        else
        {
            insert(tree[k].l,x);
            if(tree[tree[k].l].rnd<tree[k].rnd)
              rturn(k);
        }
    }
}
int tmp;
void query_pro(int k,int x)
{
    if(k==0)
      return;
    if(x>tree[k].v)
    {
       tmp=k;//不断更新过程。数值小于目标值,去右子树里找 ,找到第一个比它大的值。此时更新结果即为比它小的最大的值
       query_pro(tree[k].r,x);
    }
    else
      query_pro(tree[k].l,x);
}
void query_sub(int k,int x)
{
     if(k==0)
      return;
     if(x<tree[k].v)//与上面同理
     {
       tmp=k;
       query_sub(tree[k].l,x);
     }
     else
      query_sub(tree[k].r,x);
}
void del(int &k,int x)
{
     if(k==0)
       return;
     if(tree[k].v==x)
     {
        if(tree[k].w>1)//若不止相同值的个数有多个,删去一个
        {
          tree[k].w--;
          tree[k].size--;
          return;
        }
        if(tree[k].l*tree[k].r==0)//有一个儿子为空
          k=tree[k].l+tree[k].r;
        else
        {
            if(tree[tree[k].l].rnd<tree[k].rnd)
            {
               rturn(k);
               del(k,x);
            }
            else
            {
                lturn(k);
                del(k,x);
            }
        }
     }
     else
     {
         if(x>tree[k].v)
         {
            tree[k].size--;
            del(tree[k].r,x);
         }
         else
         {
             tree[k].size--;
             del(tree[k].l,x);
         }
     }
}
int query_rank(int k,int x)
{
    if(k==0)
      return 0;
    if(tree[k].v==x)
     return tree[tree[k].l].size+1;//找到目标值,左子树都比它小,左子树大小+1即为它的排名
    else
    {
        if(x>tree[k].v)
          return tree[tree[k].l].size+tree[k].w+query_rank(tree[k].r,x);
        else
          return query_rank(tree[k].l,x);
    }
}
int query_num(int k,int x)
{
    if(k==0)
      return 0;
    if(x<=tree[tree[k].l].size)
      return query_num(tree[k].l,x);
    else
      if(x>tree[tree[k].l].size+tree[k].w)
        return query_num(tree[k].r,x-tree[tree[k].l].size-tree[k].w);
      else
        return tree[k].v;
}
int main()
{
    freopen("phs.in","r",stdin);
    freopen("phs.out","w",stdout);
    scanf("%d",&n);
    int opt,x;
    pos(i,1,n)
    {
        scanf("%d%d",&opt,&x);
        switch(opt)
        {
        case 1:insert(root,x);break;
        case 2:del(root,x);break;
        case 3:printf("%d\n",query_rank(root,x));break;
        case 4:printf("%d\n",query_num(root,x));break;
        case 5:tmp=0;query_pro(root,x);printf("%d\n",tree[tmp].v);break;
        case 6:tmp=0;query_sub(root,x);printf("%d\n",tree[tmp].v);break;
        }
    }
    //while(1);
    return 0;
}

  

Treap详解的更多相关文章

  1. 【数据结构】FHQ Treap详解

    FHQ Treap是什么? FHQ Treap,又名无旋Treap,是一种不需要旋转的平衡树,是范浩强基于Treap发明的.FHQ Treap具有代码短,易理解,速度快的优点.(当然跟红黑树比一下就是 ...

  2. FHQ Treap 详解

    鲜花 一些鲜花放在前面,平衡树学了很久,但是每学一遍都忘,原因就在于我只能 70% 理解 + 30% 背板子,所以每次都忘.这次我采取了截然不同的策略,自己按照自己的理解打一遍,大获成功(?),大概打 ...

  3. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  4. 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)

    一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...

  5. EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解

    前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...

  6. Java 字符串格式化详解

    Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...

  7. Android Notification 详解(一)——基本操作

    Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...

  8. Android Notification 详解——基本操作

    Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...

  9. Git初探--笔记整理和Git命令详解

    几个重要的概念 首先先明确几个概念: WorkPlace : 工作区 Index: 暂存区 Repository: 本地仓库/版本库 Remote: 远程仓库 当在Remote(如Github)上面c ...

随机推荐

  1. 容器间通信的三种方式 - 每天5分钟玩转 Docker 容器技术(35)

    容器之间可通过 IP,Docker DNS Server 或 joined 容器三种方式通信. IP 通信 从上一节的例子可以得出这样一个结论:两个容器要能通信,必须要有属于同一个网络的网卡. 满足这 ...

  2. Mybatis中使用 #{} 和 ${} 向sql传参时的区别

    今天在工作时,使用MyBatis中向sql传递两个参数时,一直显示SQL语法错误,仔细检查,才发现传入的参数被加上了引号,导致传入的参数(要传入的参数是表名)附近出现语法错误. 错误写法: } a } ...

  3. 【LeetCode】187. Repeated DNA Sequences

    题目: All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, for example: " ...

  4. js代码风格之链式结构

    <div class="box"> <ul class="menu"> <li class="level1"& ...

  5. [图形学] Chp8 使用双缓存创建帧动画

    第八章的习题有动画的要求,之前并没有讲解动画如何制作,网上搜到一篇文章SCARA——OpenGL入门学习五六(三维变换.动画),按照里面的方法,使用双缓存和空闲回调函数实现了一个简单的帧动画. #in ...

  6. VB6之阴影图层

    要是能创建半透明的刷子就好了,就不必像这样以图层的方式实现透明阴影效果. 代码: 'code by lichmama@cnblogs.com '绘制阴影图层 Private Declare Funct ...

  7. 拥抱.NET Core系列:依赖注入(2)

    上一篇"拥抱.NET Core系列:依赖注入(1)"大体介绍了服务注册.获取和生命周期,这一篇来做一些补充. 由于内容跨度大(.NET Core.ASP.NET Core),所以文 ...

  8. HTTP消息中Header头部信息整理

    1.HTTP请求方式 GET 向Web服务器请求一个文件 POST 向Web服务器发送数据让Web服务器进行处理 PUT 向Web服务器发送数据并存储在Web服务器内部 HEAD 检查一个对象是否存在 ...

  9. docker~使用阿里加速器安centos

    回到目录 上一篇说了hub.docker.com里拉个镜像太,而阿里云为我们做了不少本国镜像,这样下载的速度就很惊人了,下面看一下在centos7下配置阿里云加速器的方法 打开服务配置文件 vi /e ...

  10. Android - 自定义控件之圆形控件

    自定义控件 - 圈圈 Android L: Android Studio 效果:能够自定义圆圈半径和位置:设定点击效果:改变背景颜色 下面是demo图 点击前: 点击后: 自定义控件一般要继承View ...