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平衡树学习笔记的更多相关文章

  1. 平衡树学习笔记(3)-------Splay

    Splay 上一篇:平衡树学习笔记(2)-------Treap Splay是一个实用而且灵活性很强的平衡树 效率上也比较客观,但是一定要一次性写对 debug可能不是那么容易 Splay作为平衡树, ...

  2. 平衡树学习笔记(6)-------RBT

    RBT 上一篇:平衡树学习笔记(5)-------SBT RBT是...是一棵恐怖的树 有多恐怖? 平衡树中最快的♂ 不到200ms的优势,连权值线段树都无法匹敌 但是,通过大量百度,发现RBT的代码 ...

  3. 平衡树学习笔记(5)-------SBT

    SBT 上一篇:平衡树学习笔记(4)-------替罪羊树 所谓SBT,就是Size Balanced Tree 它的速度很快,完全碾爆Treap,Splay等平衡树,而且代码简洁易懂 尤其是插入节点 ...

  4. 平衡树学习笔记(2)-------Treap

    Treap 上一篇:平衡树学习笔记(1)-------简介 Treap是一个玄学的平衡树 为什么说它玄学呢? 还记得上一节说过每个平衡树都有自己的平衡方式吗? 没错,它平衡的方式是......rand ...

  5. 普通平衡树学习笔记之Splay算法

    前言 今天不容易有一天的自由学习时间,当然要用来"学习".在此记录一下今天学到的最基础的平衡树. 定义 平衡树是二叉搜索树和堆合并构成的数据结构,它是一 棵空树或它的左右两个子树的 ...

  6. [普通平衡树splay]【学习笔记】

    参考: http://blog.csdn.net/clove_unique/article/details/50630280 gty课件 找一个好的风格太难了,自己习惯用struct,就强行用stru ...

  7. 文艺平衡Splay树学习笔记(2)

    本blog会讲一些简单的Splay的应用,包括但不局限于 1. Splay 维护数组下标,支持区间reserve操作,解决区间问题 2. Splay 的启发式合并(按元素多少合并) 3. 线段树+Sp ...

  8. splay tree 学习笔记

    首先感谢litble的精彩讲解,原文博客: litble的小天地 在学完二叉平衡树后,发现这是只是一个不稳定的垃圾玩意,真正实用的应有Treap.AVL.Splay这样的查找树.于是最近刚学了学了点S ...

  9. 浅谈树套树(线段树套平衡树)&学习笔记

    0XFF 前言 *如果本文有不好的地方,请在下方评论区提出,Qiuly感激不尽! 0X1F 这个东西有啥用? 树套树------线段树套平衡树,可以用于解决待修改区间\(K\)大的问题,当然也可以用 ...

随机推荐

  1. mobiscroll

    https://docs.mobiscroll.com/3-2-3/jquery/calendar#!options

  2. 接口自动化---简单的数据驱动框架ATP(基于excel)

    数据驱动测试:根据数据进行测试.将用例写入excel文件,用代码读取文件中的数据,从而实现自动化测试. 自动化框架实现步骤: 1.获取用例 2.调用接口 3.校验结果 4.发送测试报告 5.异常处理 ...

  3. JS -- 操作符和数组

    一.Javascript常用操作符 <!DOCTYPE html> <html> <head> <meta charset="UTF-8" ...

  4. Apache和分布式部署

    1.tomcat分布式部署 1.1.要配置几个tomcat,就部署几个相同程序名的tomcat 1.2.配置每个tomcat下server.xml中ajp端口,以及后面的jvmRoute,第几个就配置 ...

  5. pycharm代码中批量粘贴内容的快捷键

    windows电脑中,竖向批量复制的快捷键:Alt

  6. 图灵机器人api的使用方法含微信版本和网页版

    访问图灵机器人官网http://www.tuling123.com/ 注册一个新的机器人账号 注册成功后转到主页 点击我的机器人>创建机器人>微信机器人 填写基本信息 点击微信介入> ...

  7. VirtualBox中安装的CentOS开启SSH并设置访问外网

    1.全局设置NAT网络 打开VirtualBox->管理->全局设定 网络->添加按钮->添加一个NAT网络(使用默认的就行,不用改动) 2.设置用来本机于VirtualBox ...

  8. 对vue的初步学习

    vue: vue:一个mvvm框架(库),和angular类似 比较容易上手 指令以v=xxx 一片html代码配合接送,在new一个vue实例 适合:移动端,小巧 vue基本雏形 v-model 一 ...

  9. 从CSDN博客下载的图片如何无损去水印

    如果你想下载别人CSDN博客文章中很好看的图片,但却有水印 想要下载去水印的图片,可以先鼠标右击该图片,选择复制图片地址 https://img-blog.csdnimg.cn/20200916140 ...

  10. 趣图:调试bug进行时

      扩展阅读 趣图:大神写实,左脚程序继续运行,右脚程序调试 趣图:Bug 多了,总有一个会把你坑了 趣图:领导在旁,只求代码无Bug