BST,Splay平衡树学习笔记
BST,Splay平衡树学习笔记
1.二叉查找树BST
BST是一种二叉树形结构,其特点就在于:每一个非叶子结点的值都大于他的左子树中的任意一个值,并都小于他的右子树中的任意一个值。
2.BST的用处
如果利用朴素算法序列中的第k大的数,最坏的情况下可能达到O(N*logN),而由于BST的特性,我们可以把复杂度优化为O(logN),不仅如此,我们还可以在O(logn)的复杂度下查找元素,O(1)的复杂度下修改元素。对于有些数据来说,极大地节约了时间。
3.BST的优化---splay平衡树
再优秀的算法都会有自己的缺点,而BST的缺点就在于,在极端情况下,树形结构会退化为一条链,此时进行操作甚至比朴素算法还要劣。那啷个办嘞?肯定要想办法降低树的深度于是天空一声巨响,,神奇的splay,treap闪亮登场qwq。
每一次的询问值x,我们把他移到root的位置,在这样做的同时,我们维护好这个BST的性质(忘了的自己看第一条0_0)。就比如说,我们把这条链调成splay的形式:
是不是感觉优化过后好厉害~咳咳,下面还有更厉害的
优化核心:旋转操作Rotate
定义rotate(now,goal),将节点now旋转至goal节点处,同时,为了维护BST的性质,我们需要将树的结构进行调整:(这里只考虑把now转移到他的父亲fa)
step1:询问now,fa是他们父亲的哪个儿子假设查询结果为l_now,l_fa
step2:把now放到他父亲的位置上,维护节点信息之后我们来处理他原来的爸爸fa
step3:按道理来讲,fa由于比now大(其他情况自己画图我懒得动QAQ),fa本来应该变为now的右儿子(由于BST的性质),可是此时now已经有一个孩子ch了,如果强行接入,ch就没爸爸了,他会很伤心的,为了维护一棵快乐的BST,我们决定再给他找个爸爸,在上面操作中,fa失去了一个儿子(now变为了他的父亲),他现在也是难过的摇头晃脑,所以,我们不妨直接让fa领养ch,由于ch为原树中fa左子树中的结点,并且为now的右儿子,所以有如下关系:
now<ch<fa,刚好,将ch拿给fa当儿子完美的维护了BST的性质,所以,这是一个合法的操作(我们就可以给他发领养证啦:)),最后再维护一下结点信息就好啦QAQ
slpay操作就是不断地上旋直到达到目标,rotate则是单旋
下面上图:
//rotate操作我感觉讲的不是很清楚,这里推荐一下Clove学姐的文章,下面附上链接https://www.cnblogs.com/hua-dong/p/7822815.html
下面来道题练习一下:https://www.luogu.com.cn/problem/P3391
说实话这道题入门有点难,其实这里bst我维护的是输出的优先级,而翻转操作则是交换优先级,所以立刻想到交换中间结点的左右子树,那怎么保证以这个结点为根节点的子树完全且恰好包含题目要翻转的区间呢?
不妨这样考虑:对于每一个要翻转的区间,[l,r],我们把l-1号节点旋转到根节点处,再把r+1旋转到l-1的又孩子处,你们自己按照rotate转一下之后就会发现,我们要维护的区间恰好是根节点的右子树的左子树 'O' (手动惊讶Σ(⊙▽⊙"a)
就okk啦~
下面上代码,有注释滴,码风还好不用担心:
1 #include<iostream>
2 #include<cstdio>
3 #define N 100005
4
5 using namespace std;
6
7 int ch[N][2],fa[N],size[N],ans[N],val[N],n,m,root,cnt=0;
8 bool tag[N]={0};
9
10 int Read()
11 {
12 int num=0,k=1;
13 char c=getchar();
14 while(c!='-'&&((c>'9')||(c<'0'))) c=getchar();
15 if(c=='-')
16 {
17 k=-1;
18 c=getchar();
19 }
20 while(c>='0'&&c<='9')
21 {
22 num=(num<<3)+(num<<1)+c-'0',c=getchar();
23 }
24 return num*k;
25 }
26
27 void push_up(int x)
28 {
29 size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
30 } //以x为root的子树中结点的个数
31
32 int locate(int x)
33 {
34 return ch[fa[x]][1]==x;
35 } // 定位x是他爸爸的哪个儿子
36
37 void rotate(int x) // 把x旋转到root
38 {
39 int y=fa[x],z=fa[y],b=locate(x),c=locate(y),a=ch[x][!b];
40 if(fa[y]) ch[z][c]=x ; //如果x的爸爸不是root
41 else
42 {
43 root=x;
44 fa[x]=z; //替代root的位置
45 }
46 if(a) fa[a]=y;ch[y][b]=a;//旋转
47 ch[x][!b]=y;
48 fa[y]=x;
49 push_up(y);
50 push_up(x);
51 }
52
53 int build(int l,int r,int f)//构造splay
54 {
55 int mid=(l+r)>>1;
56 val[mid]=ans[mid];
57 fa[mid]=f;
58 if(l<mid) ch[mid][0]=build(l,mid-1,mid);
59 if(mid<r) ch[mid][1]=build(mid+1,r,mid);
60 push_up(mid);
61 return mid;
62 }
63
64 void push_down(int x)
65 {
66 if(!tag[x]) return ;//x结点上覆盖了懒标记
67 tag[ch[x][0]]^=1;
68 tag[ch[x][1]]^=1;
69 swap(ch[x][0],ch[x][1]);
70 tag[x]=false;
71 }
72
73 int query(int x) //询问x在splay中的结点编号
74 {
75 int now=root;
76
77 while(true)
78 {
79 push_down(now);
80 if(ch[now][0]&&x<=size[ch[now][0]]) now=ch[now][0];//对于splay上值为a的结点有size[ch[now][0]]>=a这里判断是否在左子树
81 else
82 {
83 int num=(ch[now][0] ? size[ch[now][0]] :0) +1;//计算当前结点的编号:左子树根节点编号+1
84 if(num==x) return now;//
85 x-=num;
86 now=ch[now][1];
87 }
88 }
89 }
90
91 void splay(int x,int goal)//把now转为goal的子节点
92 {
93 while(fa[x]!=goal)
94 {
95 int y=fa[x],z=fa[y];
96 if(z==goal) rotate(x);//单旋
97 else
98 {
99 if(locate(x)==locate(y))
100 {
101 rotate(y);
102 rotate(x);
103 }
104 else
105 {
106 rotate(x);
107 rotate(x);
108 }
109 }
110 }
111 }
112
113 void print(int now)
114 {
115 push_down(now);
116 if(ch[now][0]) print(ch[now][0]);
117
118 ans[++cnt]=val[now];
119
120 if(ch[now][1]) print(ch[now][1]);
121
122 }
123
124 int main ()
125 {
126 int l,r;
127 n=Read(),m=Read();
128 for(int i=1;i<=n+2;i++) ans[i]=i-1;// 记录前驱
129
130 root=build(1,n+2,0);
131
132 for(int i=1;i<=m;i++)
133 {
134 l=Read(),r=Read();
135 int x=query(l),y=query(r+2); //查找x的前驱所在的位置,和y后驱所在的位置,因为预处理时ans存的是前趋,所以直接查找x,而y的后驱变成了y+2
136 splay(x,0);
137 splay(y,x); //将x前驱上旋至根节点,y的后驱上旋成根节点右儿子的左子树
138 tag[ch[ch[root][1]][0]]^=1;//经过旋转后,此时根节点的右儿子的左子树就是需要翻转的区间,所以lazy标记
139
140 }
141
142 print(root);
143
144 for(int i=1;i<=n;i++) printf("%d ",ans[i+1]);
145 return 0;
146 }
好啦,BST就到这里啦~第一次写文章,写的不好的地方原谅一下,也欢迎大家提问和指正,拜拜~
ps.转载记得注明出处哦,你看人家那么辛苦滴QAQ
BST,Splay平衡树学习笔记的更多相关文章
- 平衡树学习笔记(3)-------Splay
Splay 上一篇:平衡树学习笔记(2)-------Treap Splay是一个实用而且灵活性很强的平衡树 效率上也比较客观,但是一定要一次性写对 debug可能不是那么容易 Splay作为平衡树, ...
- 平衡树学习笔记(6)-------RBT
RBT 上一篇:平衡树学习笔记(5)-------SBT RBT是...是一棵恐怖的树 有多恐怖? 平衡树中最快的♂ 不到200ms的优势,连权值线段树都无法匹敌 但是,通过大量百度,发现RBT的代码 ...
- 平衡树学习笔记(5)-------SBT
SBT 上一篇:平衡树学习笔记(4)-------替罪羊树 所谓SBT,就是Size Balanced Tree 它的速度很快,完全碾爆Treap,Splay等平衡树,而且代码简洁易懂 尤其是插入节点 ...
- 平衡树学习笔记(2)-------Treap
Treap 上一篇:平衡树学习笔记(1)-------简介 Treap是一个玄学的平衡树 为什么说它玄学呢? 还记得上一节说过每个平衡树都有自己的平衡方式吗? 没错,它平衡的方式是......rand ...
- 普通平衡树学习笔记之Splay算法
前言 今天不容易有一天的自由学习时间,当然要用来"学习".在此记录一下今天学到的最基础的平衡树. 定义 平衡树是二叉搜索树和堆合并构成的数据结构,它是一 棵空树或它的左右两个子树的 ...
- [普通平衡树splay]【学习笔记】
参考: http://blog.csdn.net/clove_unique/article/details/50630280 gty课件 找一个好的风格太难了,自己习惯用struct,就强行用stru ...
- 文艺平衡Splay树学习笔记(2)
本blog会讲一些简单的Splay的应用,包括但不局限于 1. Splay 维护数组下标,支持区间reserve操作,解决区间问题 2. Splay 的启发式合并(按元素多少合并) 3. 线段树+Sp ...
- splay tree 学习笔记
首先感谢litble的精彩讲解,原文博客: litble的小天地 在学完二叉平衡树后,发现这是只是一个不稳定的垃圾玩意,真正实用的应有Treap.AVL.Splay这样的查找树.于是最近刚学了学了点S ...
- 浅谈树套树(线段树套平衡树)&学习笔记
0XFF 前言 *如果本文有不好的地方,请在下方评论区提出,Qiuly感激不尽! 0X1F 这个东西有啥用? 树套树------线段树套平衡树,可以用于解决待修改区间\(K\)大的问题,当然也可以用 ...
随机推荐
- InnoDB 引擎中的索引类型
首先索引是一种数据结构,并且索引不是越多越好.合理的索引可以提高存储引擎对数据的查询效率. 形象一点来说呢,索引跟书本的目录一样,能否快速的查找到你需要的信息,取决于你设计的目录是否合理. MySQL ...
- Tcahce Stashing Unlink Attack
今年校赛有点可惜,最后两道质量不错的pwn每做出来,总的来说还是我太菜了,希望下次校赛能AK pwn题.不过这次校赛也没有白打,还是有学到新的东西的.在这里感谢出题的学长. glibc-2.29以后u ...
- python基础:日志模块logging,nnlog
python里面用来打印日志的模块,就是logging模块,logging模块可以在控制台打印日志,也可以写入文件中.也可以两个操作都执行 1.控制台输入 import logging#导入模块 lo ...
- python3中抛异常except后面参数
try: xxx except exception as e: print("给exception取了个别名叫做e") else: ccc
- oracle之二实例管理及数据库的启动/关闭
实例管理及数据库的启动/关闭 2.1 实例和参数文件 1.instance 功能:用于管理和访问database.instance在启动阶段读取初始化参数文件(init parameter fil ...
- Vue iview Tree组件实现文件目录-高级实现
Tree组件实现文件目录-基础实现 封装文件目录组件 src\views\folder-tree\folder-tree.vue <template> <div class=&quo ...
- 【Nginx】冰河又一本超硬核Nginx PDF教程免费开源!!
写在前面 在 [冰河技术] 微信公众号中的[Nginx]专题,更新了不少文章,有些读者反馈说,在公众号中刷 历史文章不太方便,有时会忘记自己看到哪一篇了,当打开一篇文章时,似乎之前已经看过了, 但就是 ...
- 3.Channel详解
- 【大数据】MapReduce开发小实战
Before:前提:hadoop集群应部署完毕. 一.实战科目:做一个Map Reduce分布式开发,开发内容为统计文件中的单词出现次数. 二.战前准备 1.本人在本地创建了一个用于执行MR的的文件, ...
- Object类:又回到最初的起点
Object类大概是每个JAVA程序员认识的第一个类,因为它是所有其他类的祖先类.在JAVA单根继承的体系下,这个类中的每个方法都显得尤为重要,因为每个类都能够调用或者重写这些方法.当你JAVA学到一 ...