数据结构:Treap
关于重量平衡树的相关概念可以参考姊妹文章:重量平衡树之替罪羊树
Treap是依靠旋转来维护平衡的重量平衡树中最为好写的一中,因为它的旋转不是LL就是RR
对于每一个新的节点,它给这个节点分配了一个随机数,用作优先级,然后以这个优先级来维护一个堆结构
由于堆本身就是完全二叉树结构,这样维护之后的树就无限接近于完全二叉树,所以还是很神奇的
这棵树满足BST的一切性质,除了不能处理序列问题之外已经无敌了
应该说,抛去动态树问题之外,这是实战最好用的树了
我们还是先看定义:
struct Tree
{
int v,w;
int size;
int rnd;
int ch[];
}t[maxn];
int root;
int size;
int ans=;
在这里v是值,w是同值的节点个数,size是子树的节点总数,rnd是优先级,外面:root是根节点,size是根节点中元素个数,ans是统计答案用的临时变量
我们这里还是先介绍插入操作,平衡树问题如果不是处理序列的,建议就一个一个插
void insert(int &k,int x)
{
if(k==)
{
size++;
k=size;
t[k].size=t[k].w=;
t[k].v=x;
t[k].rnd=rand();
return;
}
t[k].size++;
if(t[k].v==x)
t[k].w++;
else if(x>t[k].v)
{
insert(t[k].ch[],x);
if(t[t[k].ch[]].rnd<t[k].rnd)
lturn(k);
}
else
{
insert(t[k].ch[],x);
if(t[t[k].ch[]].rnd<t[k].rnd)
rturn(k);
}
}
插入时根据是否是叶子节点,遍历到的节点的w值等进行维护
每次插入要判断一下是否满足堆结构,进行相应的旋转调整
下面给出旋转调整的函数,基本上可以作为左旋和右旋的模板了
void rturn(int &k)
{
int tmp=t[k].ch[];
t[k].ch[]=t[tmp].ch[];
t[tmp].ch[]=k;
t[tmp].size=t[k].size;
update(k);
k=tmp;
}
void lturn(int &k)
{
int tmp=t[k].ch[];
t[k].ch[]=t[tmp].ch[];
t[tmp].ch[]=k;
t[tmp].size=t[k].size;
update(k);
k=tmp;
}
然后我们给出update函数,这里要维护的东西很少,只有一个size,所以这个时候的update就是更新size用的
void update(int k)
{
t[k].size=t[t[k].ch[]].size+t[t[k].ch[]].size+t[k].w;
}
然后是四种基本查询工作,各种平衡树基本一致,也可以作为模板记下来了
int query_rank(int k,int x)
{
if(k==)
return ;
if(t[k].v==x)
return t[t[k].ch[]].size+;
else if(x>t[k].v)
return t[t[k].ch[]].size+t[k].w+query_rank(t[k].ch[],x);
else
return query_rank(t[k].ch[],x);
}
int query_num(int k,int x)
{
if(k==)
return ;
if(x<=t[t[k].ch[]].size)
return query_num(t[k].ch[],x);
else if(x>t[t[k].ch[]].size+t[k].w)
return query_num(t[k].ch[],x-t[t[k].ch[]].size-t[k].w);
else
return t[k].v;
}
void query_pro(int k,int x)
{
if(k==)
return;
if(t[k].v<x)
ans=k,query_pro(t[k].ch[],x);
else
query_pro(t[k].ch[],x);
}
void query_sub(int k,int x)
{
if(k==)
return;
if(t[k].v>x)
ans=k,query_sub(t[k].ch[],x);
else
query_sub(t[k].ch[],x);
}
最后我们给出完整的模板,这棵树一定要熟练掌握,只要是平衡树问题,很大可能都是用它来完成的
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
const int maxn=;
int n;
struct Tree
{
int v,w;
int size;
int rnd;
int ch[];
}t[maxn];
int root;
int size;
int ans=;
void update(int k)
{
t[k].size=t[t[k].ch[]].size+t[t[k].ch[]].size+t[k].w;
}
void rturn(int &k)
{
int tmp=t[k].ch[];
t[k].ch[]=t[tmp].ch[];
t[tmp].ch[]=k;
t[tmp].size=t[k].size;
update(k);
k=tmp;
}
void lturn(int &k)
{
int tmp=t[k].ch[];
t[k].ch[]=t[tmp].ch[];
t[tmp].ch[]=k;
t[tmp].size=t[k].size;
update(k);
k=tmp;
}
void insert(int &k,int x)
{
if(k==)
{
size++;
k=size;
t[k].size=t[k].w=;
t[k].v=x;
t[k].rnd=rand();
return;
}
t[k].size++;
if(t[k].v==x)
t[k].w++;
else if(x>t[k].v)
{
insert(t[k].ch[],x);
if(t[t[k].ch[]].rnd<t[k].rnd)
lturn(k);
}
else
{
insert(t[k].ch[],x);
if(t[t[k].ch[]].rnd<t[k].rnd)
rturn(k);
}
}
void del(int &k,int x)
{
if(k==)
return;
if(t[k].v==x)
{
if(t[k].w>)
{
t[k].w--;
t[k].size--;
return;
}
if(t[k].ch[]*t[k].ch[]==)
k=t[k].ch[]+t[k].ch[];
else if(t[t[k].ch[]].rnd<t[t[k].ch[]].rnd)
rturn(k),del(k,x);
else
lturn(k),del(k,x);
}
else if(x>t[k].v)
t[k].size--,del(t[k].ch[],x);
else
t[k].size--,del(t[k].ch[],x);
}
int query_rank(int k,int x)
{
if(k==)
return ;
if(t[k].v==x)
return t[t[k].ch[]].size+;
else if(x>t[k].v)
return t[t[k].ch[]].size+t[k].w+query_rank(t[k].ch[],x);
else
return query_rank(t[k].ch[],x);
}
int query_num(int k,int x)
{
if(k==)
return ;
if(x<=t[t[k].ch[]].size)
return query_num(t[k].ch[],x);
else if(x>t[t[k].ch[]].size+t[k].w)
return query_num(t[k].ch[],x-t[t[k].ch[]].size-t[k].w);
else
return t[k].v;
}
void query_pro(int k,int x)
{
if(k==)
return;
if(t[k].v<x)
ans=k,query_pro(t[k].ch[],x);
else
query_pro(t[k].ch[],x);
}
void query_sub(int k,int x)
{
if(k==)
return;
if(t[k].v>x)
ans=k,query_sub(t[k].ch[],x);
else
query_sub(t[k].ch[],x);
}
int main()
{
cin>>n;
int tmp,x;
for(int i=;i<=n;i++)
{
cin>>tmp>>x;
switch(tmp)
{
case :insert(root,x);break;
case :del(root,x);break;
case :cout<<query_rank(root,x)<<endl;break;
case :cout<<query_num(root,x)<<endl;break;
case :ans=;query_pro(root,x);cout<<t[ans].v<<endl;break;
case :ans=;query_sub(root,x);cout<<t[ans].v<<endl;break;
}
}
return ;
}
数据结构:Treap的更多相关文章
- [数据结构]Treap简介
[写在前面的话] 如果想学Treap,请先了解BST和BST的旋转 二叉搜索树(BST)(百度百科):[here] 英文好的读者可以戳这里(维基百科) 自己的博客:关于旋转(很水,顶多就算是了解怎么旋 ...
- 模板 - 数据结构 - Treap
还有人把Treap叫做树堆的,但是常用名还是叫做Treap的比较多. 不进行任何封装的,带求和操作的,一个节点存放多个元素的最普通的Treap. #include<bits/stdc++.h&g ...
- 【bzoj3173-最长上升子序列-一题两解】
这道题不就是简单的DP吗,BZOJ在水我!不,你是错的. ·本题特点: 不断向不同位置插入数字(按数字1,2,3,4,5,6……),需要求出每一次插入后的最长上升子序列. ·分析 ...
- [CSP-S模拟测试]:椎(线段树维护区间最值和单调栈)
题目描述 虽不能至,心向往之. $Treap=Tree+Heap$ 椎$=$树$+$堆 小$\pi$学习了计算机科学中的数据结构$Treap$. 小$\pi$知道$Treap$指的是一种树. 小$\p ...
- 数据结构之Treap
1. 概述 同splay tree一样,treap也是一个平衡二叉树,不过Treap会记录一个额外的数据,即优先级.Treap在以关键码构成二叉搜索树的同时,还按优先级来满足堆的性质.因而,Treap ...
- 模板 - 数据结构 - 可持久化无旋Treap/PersistentFHQTreap
有可能当树中有键值相同的节点时,貌似是要对Split和Merge均进行复制的,本人实测:只在Split的时候复制得到了一个WA,但只在Merge的时候复制还是AC,可能是恰好又躲过去了.有人说假如确保 ...
- 【数据结构】FHQ Treap详解
FHQ Treap是什么? FHQ Treap,又名无旋Treap,是一种不需要旋转的平衡树,是范浩强基于Treap发明的.FHQ Treap具有代码短,易理解,速度快的优点.(当然跟红黑树比一下就是 ...
- 【数据结构】【平衡树】无旋转treap
最近在研究平衡树,看起来这种东西又丧水又很深,感觉很难搞清楚.在Ditoly学长的建议下,我先学习了正常的treap,个人感觉这应该是平衡树当中比较好懂的而且比较好写的一种. 然而,发现带旋treap ...
- FHQ Treap小结(神级数据结构!)
首先说一下, 这个东西可以搞一切bst,treap,splay所能搞的东西 pre 今天心血来潮, 想搞一搞平衡树, 先百度了一下平衡树,发现正宗的平衡树写法应该是在二叉查找树的基础上加什么左左左右右 ...
- 【数据结构】【平衡树】treap
之前写treap的传送门 之前写的那个太毒瘤了,这次放一个更毒瘤的指针版上来 #include<cstdio> #include<iostream> #define rg re ...
随机推荐
- 第三课——MFC编程
一.MFC概述 1. MFC简述 MFC不仅仅是一套基础类库,更是一种编程方式. 2. MFC由来 1987年微软公司推出了第一代Windows产品,并为应用程序设计者提供了Win16(16位Wind ...
- 2017秋-软件工程第四次作业(2)-结对使用TDD框架完成单元测试
第一次接触“单元测试”这个要求,我和队友学习了一些示例后开始操作.如下展示一些建立单元测试的过程.Step1:右键单击[解决方案]->左键单击[添加(D)]->[新建项目(N)]. Ste ...
- P4语法(2) Parser
这里参考学习了: P4语言规范 P4台湾社群 Parser 关于parser 在P4程序中,有着大量的首部(header)和首部实例,但每次只有部分首部实例会对数据包进行操作,而parser会用于生成 ...
- lintcode-182-删除数字
182-删除数字 给出一个字符串 A, 表示一个 n 位正整数, 删除其中 k 位数字, 使得剩余的数字仍然按照原来的顺序排列产生一个新的正整数. 找到删除 k 个数字之后的最小正整数. N < ...
- lintcode-36-翻转链表 II
36-翻转链表 II 翻转链表中第m个节点到第n个节点的部分 注意事项 m,n满足1 ≤ m ≤ n ≤ 链表长度 样例 给出链表1->2->3->4->5->null, ...
- TCP源码—连接建立
一.SYN报文处理: 公共部分:tcp_v4_rcv->tcp_v4_do_rcv->tcp_v4_cookie_check(无处理动作)->tcp_rcv_state_proces ...
- 一个项目的Makefile编写及调试
父Makefile 在src目录下包含很多文件夹,那么需要遍历所有的目录执行Makefile,那么给一个在src目录下的Makefile. # 需要排除的目录 exclude_dirs := incl ...
- RHEL 6.4(i386)安装MySQL 5.6的方法
- intellij idea 如何将一个普通项目转换为maven项目
1.工程文件下新建文件pom.xml,并填写好内容. 2.在pom.xml 文件上右键 Add as Maven Project.
- Sqoop 1.4.6 安装配置
配置环境变量 # SQOOP SQOOP_HOME=/home/hadoop/development/src/sqoop-1.4.6-cdh5.6.0 PATH=$PATH:$SQOOP_HOME/b ...