Treap,简单的来说就是Tree+Heap,是一颗平衡树,每个节点有两个信息:1.key:当前节点的关键字 ;2.fix:当前节点优先级。key满足二叉排序数的性质,即左儿子都比当前节点小,右儿子都比当前节点大(或相等),fix是一个随机的数,满足小根堆(或大根堆)的性质,fix是为了防止Treap退化成链表的简单优化策略。

如下面的一颗Treap:

Treap可以进行下面几种操作:插入,查询第k大,旋转,还有其他一些基本操作和一些高级的操作,这里暂不作介绍。

1.插入元素

Treap的关联形式是链表,所以要定义一个结构体,其中包含一个指向Treap类型的指针,树的大小,左右儿子的指针。

struct Treap
{
int size,key,fix;
Treap *ch[2];
Treap(int k)//构造函数
{
size=1;
fix=rand();
key=k;
ch[0]=ch[1]=NULL;
}
int compare(int x)const
{
return key==x?-1:x<key?0:1;
}
void Maintain() //计算Treap的大小
{
size=1;
if(ch[0]!=NULL)size+=ch[0]->size;
if(ch[1]!=NULL)size+=ch[1]->size;
}
};

插入的时候,如果当前元素是空的,就用new运算符构造一颗新树(没有儿子节点),如果不是空的,就递归向下直到是 叶子节点。节点之间的联系是以链表的形式建立起来的。

如果新插入的元素的优先级不满足小根堆的性质,则要进行旋转操作,使优先级满足要求。

void insert(Treap *&t,int x)
{
if(t==NULL)t=new Treap(x);
else
{
int d=x < t->key?0:1;
insert(t->ch[d],x);
if(t->fix > t->ch[d]->fix) //破坏了优先级顺序
Rotate(t,d);
}
t->Maintain();
}

2.旋转

当优先级破坏了小根堆的性质的时候,就要进行旋转操作,使重新满足小根堆。

旋转的时候有两种情况

①:左左旋转

②:右右旋转

两种情况可以综合到一起,详细见代码。

void Rotate(Treap *&t,int d)
{
Treap *k=t->ch[d]; //临时变量
t->ch[d]=k->ch[d^1]; //用要旋转的节点的“反”儿子替换它的位置
k->ch[d^1]=t; //旋转上去
t->Maintain(); //先计算t的大小,因为现在t是k的子节点。
k->Maintain();
t=k; //根节点上移
}

这里参数的含义是,要处理的根节点是t,ch[d]需要旋转。将参数定义成指针的引用是为了方便修改t的地址。

3.查找第K大元素

利用Treap的二叉排序树的性质,左儿子都小于根节点,右儿子大于等于根节点,即可找出第K大元素。

int Kth(Treap*t,int k)
{
if(t==NULL || k<=0 || t->size<k)return -1; //找不到
if(t->ch[0]==NULL && k==1)return t->key; //是当前值
if(t->ch[0]==NULL)return Kth(t->ch[1],k-1); //在右子树找,注意要先出去根节点,所以是k-1
if(t->ch[0]->size >=k )return Kth(t->ch[0],k); //在左子树找,因为左子树上的值都小于当前节点,所以仍然是查找第K大
if(t->ch[0]->size+1==k)return t->key;
return Kth(t->ch[1],k-1-t->ch[0]->size); //注意这里k要减1
}

4.删除Treap

为了减小空间的占用,在使用完了Treap之后,要及时的把它删掉,因为是链表,所以只能一个一个的删除。

void DeleteTreap(Treap*&t)
{
if(t==NULL)return;
if(t->ch[0]!=NULL)DeleteTreap(t->ch[0]); //删除左子树
if(t->ch[1]!=NULL)DeleteTreap(t->ch[1]); //删除右子树
delete t; //释放内存
t=NULL;
}

例题:poj1442 Black Box

题目大意:给n个数,m个查询,每次查询前x个数里面第k大的数,x是升序排列的。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<string>
#define rep(i,n) for(i=1;i<=n;++i)
using namespace std;
const int maxn=1000005;
int n,m,val[maxn];
struct Treap
{
int size,key,fix;
Treap *ch[2];
Treap(int k)
{
size=1;
fix=rand();
key=k;
ch[0]=ch[1]=NULL;
}
int compare(int x)const
{
return key==x?-1:x<key?0:1;
}
void Maintain()
{
size=1;
if(ch[0]!=NULL)size+=ch[0]->size;
if(ch[1]!=NULL)size+=ch[1]->size;
}
};
void Rotate(Treap *&t,int d)
{
Treap *k=t->ch[d];
t->ch[d]=k->ch[d^1];
k->ch[d^1]=t;
t->Maintain();
k->Maintain();
t=k;
}
void insert(Treap *&t,int x)
{
if(t==NULL)t=new Treap(x);
else
{
int d=x < t->key?0:1;
insert(t->ch[d],x);
if(t->fix > t->ch[d]->fix)
Rotate(t,d);
}
t->Maintain();
}
int Kth(Treap*t,int k)
{
if(t==NULL || k<=0 || t->size<k)return -1;
if(t->ch[0]==NULL && k==1)return t->key;
if(t->ch[0]==NULL)return Kth(t->ch[1],k-1);
if(t->ch[0]->size >=k )return Kth(t->ch[0],k);
if(t->ch[0]->size+1==k)return t->key;
return Kth(t->ch[1],k-1-t->ch[0]->size);
}
void DeleteTreap(Treap*&t)
{
if(t==NULL)return;
if(t->ch[0]!=NULL)DeleteTreap(t->ch[0]);
if(t->ch[1]!=NULL)DeleteTreap(t->ch[1]);
delete t;
t=NULL;
}
int main()
{
// freopen("A.in","r",stdin);
// freopen("A.out","w",stdout);
while(scanf("%d%d",&n,&m)!=EOF)
{
int i,index=1,j;
rep(i,n)scanf("%d",&val[i]);
Treap *root=NULL;
rep(i,m)
{
int p;
scanf("%d",&p);
for(j=index;j<=p;j++)
insert(root,val[j]);
index=p+1; //更新index
printf("%d\n",Kth(root,i));
}
DeleteTreap(root); //删除Treap
}
return 0;
}

  

初识Treap的更多相关文章

  1. Treap详解

    今天一天怼了平衡树.深深地被她的魅力折服了.我算是领略到了高级数据结构的美妙.oi太神奇了. 今天初识平衡树,选择了Treap. Treap又叫树堆,是一个二叉搜索树.我们知道,它的节点插入是随机的, ...

  2. 入门平衡树: Treap

    入门平衡树:\(treap\) 前言: 如有任何错误和其他问题,请联系我 微信/QQ同号:615863087 前置知识: 二叉树基础知识,即简单的图论知识. 初识\(BST\): \(BST\)是\( ...

  3. fhq treap最终模板

    新学习了fhq treap,厉害了 先贴个神犇的版, from memphis /* Treap[Merge,Split] by Memphis */ #include<cstdio> # ...

  4. Android动画效果之初识Property Animation(属性动画)

    前言: 前面两篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画).Frame Animation(逐帧动画)Andr ...

  5. 初识Hadoop

    第一部分:              初识Hadoop 一.             谁说大象不能跳舞 业务数据越来越多,用关系型数据库来存储和处理数据越来越感觉吃力,一个查询或者一个导出,要执行很长 ...

  6. python学习笔记(基础四:模块初识、pyc和PyCodeObject是什么)

    一.模块初识(一) 模块,也叫库.库有标准库第三方库. 注意事项:文件名不能和导入的模块名相同 1. sys模块 import sys print(sys.path) #打印环境变量 print(sy ...

  7. 初识IOS,Label控件的应用。

    初识IOS,Label控件的应用. // // ViewController.m // Gua.test // // Created by 郭美男 on 16/5/31. // Copyright © ...

  8. UI篇(初识君面)

    我们的APP要想吸引用户,就要把UI(脸蛋)搞漂亮一点.毕竟好的外貌是增进人际关系的第一步,我们程序员看到一个APP时,第一眼就是看这个软件的功能,不去关心界面是否漂亮,看到好的程序会说"我 ...

  9. Python导出Excel为Lua/Json/Xml实例教程(一):初识Python

    Python导出Excel为Lua/Json/Xml实例教程(一):初识Python 相关链接: Python导出Excel为Lua/Json/Xml实例教程(一):初识Python Python导出 ...

随机推荐

  1. 1、shell 简介

    Shell 本身是一个用C语言编写的程序,它是用户使用Unix/Linux的桥 梁,用户的大部分工作都是通过Shell完成的.Shell既是一种命令语言,又是一种程序设计语言.作为命令语言,它交互式地 ...

  2. cxiamge 使用静态库 vs2010

    首先下载cxiamge,我使用的是cxiamge_702 下载地址:http://download.csdn.net/detail/xing_ping_1987/8085129 编译静态库 新建项目, ...

  3. Jquery各版本下载,附Jquery官网下载方法

    jQuery version 2.1.1 http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.1.js http://ajax.aspnetcdn.com ...

  4. R语言画正弦曲线

    正弦曲线一个周期是2π,我们要先生成x的取值范围. 可以用seq函数生成一个等差序列,步进为0.01 x=seq( 0,  2*pi,  0.01 )   pi表示π y=sin(x) plot(x, ...

  5. 栈应用之中缀表达式计算 MFC实现(计算器核心)

    大家好,我是小鸭酱,博客地址为:http://www.cnblogs.com/xiaoyajiang 支持小数.阶乘.乘方.加减乘除.括号优先级运算,美化输出结果(显示结果末尾没有多余的0) void ...

  6. Android 隐藏输入软键盘

    //隐藏输入键盘 ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)) .hideSoftInputFromWindow(BshTo ...

  7. GetSystemTime API可以得到毫秒级时间

    用Now返回的日期格式中年只有2位,即2000年显示为00, 这似乎不太令人满意. 此外Now和Time都只能获得精确到秒的时间,为了得到更精确的毫秒级时间,可以使用API函数GetSystemTim ...

  8. 如何向投资人展示——How to Present to Investors

    著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处.作者:茶叶末链接:http://www.zhihu.com/question/23638879/answer/34525204来源: ...

  9. ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase

    原文地址:http://www.51csharp.com/MVC/882.html   ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase玩转URL 引言-- 在初级篇中,我们 ...

  10. UML建模工具-火龙果软件

     官网地址:http://code.uml.com.cn/index.asp     Bridge桥梁模式    (待逆向) 桥梁模式,通过增加一个类,将抽象部分与它的实现部分分离,使它们都可以独立 ...