前几天刚刚自学了一下splay,发现思路真简单实现起来好麻烦


先贴一下头文件

# include <stdio.h>
# include <stdlib.h>
# include <iostream>
# include <string.h>
# define ll long long
# define RG register//卡常
# define IL inline//再卡常
# define UN unsigned
# define mem(a, b) memset(a, b, sizeof(a))
# define min(a, b) ((a) < (b)) ? (a) : (b)
# define max(a, b) ((a) > (b)) ? (a) : (b)
using namespace std;
  • 核心旋转操作 Splay操作(之后的操作基本上都要用到)
    1.当旋转的节点为爷爷节点的左儿子的左儿子时
    进行两次右旋操作,先转父亲,再转自己
    2.当旋转的节点为爷爷节点的左儿子的右儿子时
    进行一次左旋操作,一次右旋操作,都转自己
    3.如果要转到的点的为爷爷节点的右儿子,直接左旋
    4.剩下两种情况把以上两种情况左右互换即可
    贴一段代码
IL void Rot(RG tree *x, RG int i){ //0为左旋,1为右旋,0为左儿子,1为右儿子
RG tree *y = x -> fa;
y -> ch[!i] = x -> ch[i];
if(x -> ch[i] != NULL) x -> ch[i] -> fa = y;
x -> fa = y -> fa;
if(y -> fa != NULL)
if(y -> fa -> ch[0] == y) y -> fa -> ch[0] = x;
else y -> fa -> ch[1] = x;
x -> ch[i] = y; y -> fa = x;
if(y == root) root = x;
} IL void Splay(RG tree *x, RG tree *f){
while(x -> fa != f)
if(x -> fa -> fa == f)
if(x == x -> fa -> ch[0]) Rot(x, 1);
else Rot(x, 0);
else{
RG tree *y = x -> fa, *z = y -> fa;
if(z -> ch[0] == y)
if(y -> ch[0] == x) Rot(y, 1), Rot(x, 1);
else Rot(x, 0), Rot(x, 1);
else
if(y -> ch[1] == x) Rot(y, 0), Rot(x, 0);
else Rot(x, 1), Rot(x, 0);
}
}
  • 插入操作
    和二叉排序树一样,只不过弄完后把它Splay到根(不要问为什么)
    丑陋的代码
IL void Insert(RG int num){
RG tree *x = root;
if(x == NULL){
x = new tree;
x -> val = num;
root = x;
}
else while(2333)
if(num < x -> val){
if(x -> ch[0] == NULL){
x -> ch[0] = new tree;
x -> ch[0] -> fa = x;
x = x -> ch[0];
x -> val = num;
break;
}
x = x -> ch[0];
}
else if(num > x -> val){
if(x -> ch[1] == NULL){
x -> ch[1] = new tree;
x -> ch[1] -> fa = x;
x = x -> ch[1];
x -> val = num;
break;
}
x = x -> ch[1];
}
Splay(x, NULL);
}
  • 查找
    与二叉排序树一样
IL void Find(RG int num){
RG tree *x = root;
while(x -> val != num)
if(num < x -> val) x = x -> ch[0];
else x = x -> ch[1];
Splay(x, NULL);
}
  • 查找前驱和后缀
    前驱,跳到它的左儿子再不停地跳右儿子
    后继,跳到它的右儿子再不停地跳左儿子
IL void Findmx(RG tree *x, RG tree *f, RG int i){ //0表示后继,1表示前驱,f为该节点,x为它的左或右儿子1
while(x -> ch[i] != NULL) x = x -> ch[i];
Splay(x, f);
}
  • 删除操作
    先找到数字的位置,Splay到根,删掉它,找它左子树中的最大数(前驱)Splay到它下面作为新的根,连接右子树即可
    代码
IL void Delete(RG int num){
Find(num);
RG tree *x = root;
else if(x -> ch[0] == NULL || x -> ch[1] == NULL)
if(x -> ch[0] != NULL) root = x -> ch[0], root -> fa = NULL;
else if(x -> ch[1] != NULL) root = x -> ch[1], root -> fa = NULL;
else root = NULL;
else{
Findmx(x -> ch[0], x, 1);
root = x -> ch[0];
root -> fa = NULL;
root -> ch[1] = x -> ch[1];
if(root -> ch[1] != NULL) root -> ch[1] -> fa = root;
}
}
  • 查找某数的排名
    实行查找操作,排名就是他左子树大小加一
IL int Size(RG tree *x){
return (x == NULL) ? 0 : x -> size + 1;
} IL int Rank(RG int num){
Find(num);
return Size(root -> ch[0]) + 1;
}
  • 查找排名为k的数
    若大于当前的左子树大小加一,跳右儿子,k -= 左子树大小加一;
    否则跳右节点
IL int Pos(RG int num){
RG tree *x = root;
while(2333){
RG int l = Size(x -> ch[0]);
if(num == l + 1) break;
if(num <= l) x = x -> ch[0];
else{
num -= (l + 1);
x = x -> ch[1];
}
}
return x -> val;
}

以上就是基本操作

  • 关于更新
    如子树大小
IL int Size(RG tree *x){
return (x == NULL) ? 0 : x -> size + 1;
} IL void Updata(RG tree *x){
if(x == NULL) return;
x -> size = Size(x -> ch[0]) + Size(x -> ch[1]);
} IL void Rot(RG tree *x, RG int i){ //0为左旋,1为右旋
RG tree *y = x -> fa;
y -> ch[!i] = x -> ch[i];
if(x -> ch[i] != NULL) x -> ch[i] -> fa = y;
x -> fa = y -> fa;
if(y -> fa != NULL)
if(y -> fa -> ch[0] == y) y -> fa -> ch[0] = x;
else y -> fa -> ch[1] = x;
x -> ch[i] = y; y -> fa = x;
Updata(y); //大佬说写在这里
if(y == root) root = x;
} IL void Splay(RG tree *x, RG tree *f){
while(x -> fa != f)
if(x -> fa -> fa == f)
if(x == x -> fa -> ch[0]) Rot(x, 1);
else Rot(x, 0);
else{
RG tree *y = x -> fa, *z = y -> fa;
if(z -> ch[0] == y)
if(y -> ch[0] == x) Rot(y, 1), Rot(x, 1);
else Rot(x, 0), Rot(x, 1);
else
if(y -> ch[1] == x) Rot(y, 0), Rot(x, 0);
else Rot(x, 1), Rot(x, 0);
}
Updata(x); //大佬说要写在这里
}

简单的栗子
链接bzoj3224
请读者思考2分钟































好了直接看代码
代码调了好久QAQ

# include <stdio.h>
# include <stdlib.h>
# include <iostream>
# include <string.h>
# define ll long long
# define RG register
# define IL inline
# define UN unsigned
# define mem(a, b) memset(a, b, sizeof(a))
# define min(a, b) ((a) < (b)) ? (a) : (b)
# define max(a, b) ((a) > (b)) ? (a) : (b)
using namespace std; IL int Get(){
char c = '!'; int z = 1, num = 0;
while(c != '-' && (c < '0' || c > '9'))
c = getchar();
if(c == '-')
z = -1, c = getchar();
while(c >= '0' && c <= '9')
num = num * 10 + c - '0', c = getchar();
return num * z;
} struct tree{
tree *fa, *ch[2];
int val, tot, size;//tot用来判断重复的数
IL tree(){
fa = ch[0] = ch[1] = NULL;
size = val = tot = 0;
}
} *root; IL int Size(RG tree *x){
return (x == NULL) ? 0 : x -> size + x -> tot + 1;
} IL void Updata(RG tree *x){
if(x == NULL) return;
x -> size = Size(x -> ch[0]) + Size(x -> ch[1]);
} IL void Rot(RG tree *x, RG int i){ //0为左旋,1为右旋
RG tree *y = x -> fa;
y -> ch[!i] = x -> ch[i];
if(x -> ch[i] != NULL) x -> ch[i] -> fa = y;
x -> fa = y -> fa;
if(y -> fa != NULL)
if(y -> fa -> ch[0] == y) y -> fa -> ch[0] = x;
else y -> fa -> ch[1] = x;
x -> ch[i] = y; y -> fa = x; Updata(y);
if(y == root) root = x;
} IL void Splay(RG tree *x, RG tree *f){
while(x -> fa != f)
if(x -> fa -> fa == f)
if(x == x -> fa -> ch[0]) Rot(x, 1);
else Rot(x, 0);
else{
RG tree *y = x -> fa, *z = y -> fa;
if(z -> ch[0] == y)
if(y -> ch[0] == x) Rot(y, 1), Rot(x, 1);
else Rot(x, 0), Rot(x, 1);
else
if(y -> ch[1] == x) Rot(y, 0), Rot(x, 0);
else Rot(x, 1), Rot(x, 0);
}
Updata(x);
} IL void Insert(RG int num){
RG tree *x = root;
if(x == NULL){
x = new tree;
x -> val = num;
root = x;
}
else if(num == x -> val) x -> tot++, root = x;
else while(2333)
if(num < x -> val){
if(x -> ch[0] == NULL){
x -> ch[0] = new tree;
x -> ch[0] -> fa = x;
x = x -> ch[0];
x -> val = num;
break;
}
x = x -> ch[0];
}
else if(num > x -> val){
if(x -> ch[1] == NULL){
x -> ch[1] = new tree;
x -> ch[1] -> fa = x;
x = x -> ch[1];
x -> val = num;
break;
}
x = x -> ch[1];
}
else{
x -> tot++;
break;
}
Splay(x, NULL);
} IL void Find(RG int num){
RG tree *x = root;
while(x -> val != num)
if(num < x -> val) x = x -> ch[0];
else x = x -> ch[1];
Splay(x, NULL);
} IL void Findmx(RG tree *x, RG tree *f, RG int i){
while(x -> ch[i] != NULL) x = x -> ch[i];
Splay(x, f);
} IL void Delete(RG int num){
Find(num);
RG tree *x = root;
if(root -> tot) root -> tot--;
else if(x -> ch[0] == NULL || x -> ch[1] == NULL)
if(x -> ch[0] != NULL) root = x -> ch[0], root -> fa = NULL;
else if(x -> ch[1] != NULL) root = x -> ch[1], root -> fa = NULL;
else root = NULL;
else{
Findmx(x -> ch[0], x, 1);
root = x -> ch[0];
root -> fa = NULL;
root -> ch[1] = x -> ch[1];
if(root -> ch[1] != NULL) root -> ch[1] -> fa = root;
}
} IL int Rank(RG int num){
Find(num);
return Size(root -> ch[0]) + 1;
} IL int Pos(RG int num){
RG tree *x = root;
while(2333){
RG int l = Size(x -> ch[0]);
if(num > l && num <= l + x -> tot + 1) break;
if(num <= l) x = x -> ch[0];
else{
num -= (l + x -> tot + 1);
x = x -> ch[1];
}
}
return x -> val;
} int main(){
RG int n = Get(), opt, x;
while(n--){
opt = Get(); x = Get();
if(opt == 1) Insert(x);
if(opt == 2) Delete(x);
if(opt == 3) printf("%d\n", Rank(x));
if(opt == 4) printf("%d\n", Pos(x));
//找前驱后缀:插入数后再删除,显然有更快的(不想打其他方法了,反正能过)
if(opt == 5){
Insert(x);
Findmx(root -> ch[0], NULL, 1);
printf("%d\n", root -> val);
Delete(x);
}
if(opt == 6){
Insert(x);
Findmx(root -> ch[1], NULL, 0);
printf("%d\n", root -> val);
Delete(x);
}
}
return 0;
}
  • 关于区间操作
    一张丑陋的图

    把l-1splay到根,r+1splay到根的右儿子,则图中那个丑陋的子树就是要求的[l,r]了。

  • 删除区间
    直接断开边(显然浪费空间,自己想办法目前没遇到MLE的情况

  • 翻转区间
    用类似于线段树的懒懒的lazy标记,每次Find,splay等操作时把标记下放,更换两个子树

又一个栗子
链接bzoj3223
再思考两分钟































看代码

# include <stdio.h>
# include <stdlib.h>
# include <iostream>
# include <string.h>
# define ll long long
# define RG register
# define IL inline
# define UN unsigned
# define mem(a, b) memset(a, b, sizeof(a))
# define min(a, b) ((a) < (b)) ? (a) : (b)
# define max(a, b) ((a) > (b)) ? (a) : (b)
using namespace std; IL int Get(){
char c = '!'; int z = 1, num = 0;
while(c != '-' && (c < '0' || c > '9'))
c = getchar();
if(c == '-')
z = -1, c = getchar();
while(c >= '0' && c <= '9')
num = num * 10 + c - '0', c = getchar();
return num * z;
} struct tree{
tree *fa, *ch[2];
int size, lazy, pos;
IL tree(){
fa = ch[0] = ch[1] = NULL;
pos = lazy = size = 0;
}
IL void Pushdown(){//下放
if(!lazy) return;
lazy = 0;
if(ch[1] == NULL && ch[0] == NULL) return;
if(ch[0] != NULL) ch[0] -> lazy ^= 1;
if(ch[1] != NULL) ch[1] -> lazy ^= 1;
swap(ch[0], ch[1]);
}
} *root;
int n; IL int Size(RG tree *x){
return (x == NULL) ? 0 : x -> size + 1;
} IL void Updata(RG tree *x){
if(x == NULL) return;
x -> size = Size(x -> ch[0]) + Size(x -> ch[1]);
} IL void Dfs(RG tree *x){
if(x == NULL) return;
x -> Pushdown();
Dfs(x -> ch[0]);
if(x -> pos && x -> pos <= n)
printf("%d ", x -> pos);
Dfs(x -> ch[1]);
} IL void Rot(RG tree *x, RG int i){ //0为左旋,1为右旋
RG tree *y = x -> fa;
x -> Pushdown(); y -> Pushdown();
y -> ch[!i] = x -> ch[i];
if(x -> ch[i] != NULL) x -> ch[i] -> fa = y;
x -> fa = y -> fa;
if(y -> fa != NULL)
if(y -> fa -> ch[0] == y) y -> fa -> ch[0] = x;
else y -> fa -> ch[1] = x;
x -> ch[i] = y; y -> fa = x; Updata(y);
if(y == root) root = x;
} IL void Splay(RG tree *x, RG tree *f){
while(x -> fa != f){
x -> Pushdown();
if(x -> fa -> fa == f)
if(x == x -> fa -> ch[0]) Rot(x, 1);
else Rot(x, 0);
else{
RG tree *y = x -> fa, *z = y -> fa;
if(z -> ch[0] == y)
if(y -> ch[0] == x) Rot(y, 1), Rot(x, 1);
else Rot(x, 0), Rot(x, 1);
else
if(y -> ch[1] == x) Rot(y, 0), Rot(x, 0);
else Rot(x, 1), Rot(x, 0);
}
}
Updata(x);
} IL tree *Build(RG int l, RG int r, RG tree *f){
RG int mid = (l + r) >> 1;
tree *x = new tree;
x -> pos = mid;
x -> fa = f;
if(mid > l) x -> ch[0] = Build(l, mid - 1, x);
if(mid < r) x -> ch[1] = Build(mid + 1, r, x);
Updata(x);
return x;
} IL void Find(RG int num, RG tree *f){
RG tree *x = root;
while(2333){
x -> Pushdown();
RG int l = Size(x -> ch[0]);
if(num < l) x = x -> ch[0];
else if(num == l) break;
else{
num -= (l + 1);
x = x -> ch[1];
}
}
Splay(x, f);
} IL void Turn(){
RG int l = Get(), r = Get();
Find(l - 1, NULL); Find(r + 1, root);
root -> ch[1] -> ch[0] -> lazy ^= 1;
} int main(){
n = Get();
RG int m = Get();
root = Build(0, n + 1, NULL);
while(m--) Turn();
Dfs(root);
printf("\n");
return 0;
}

解释或代码错误还请大佬指出,本蒟蒻一定会改

Splay树简单操作的更多相关文章

  1. Splay树(多操作)——POJ 3580 SuperMemo

    相应POJ题目:点击打开链接 SuperMemo Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 11309   Accept ...

  2. 伸展树(Splay树)的简要操作

    伸展树(splay树),是二叉排序树的一种.[两个月之前写过,今天突然想写个博客...] 伸展树和一般的二叉排序树不同的是,在每次执行完插入.查询.删除等操作后,都会自动平衡这棵树.(说是自动,也就是 ...

  3. ZOJ3765 Lights Splay树

    非常裸的一棵Splay树,需要询问的是区间gcd,但是区间上每个数分成了两种状态,做的时候分别存在val[2]的数组里就好.区间gcd的时候基本上不支持区间的操作了吧..不然你一个区间里加一个数gcd ...

  4. [Splay伸展树]splay树入门级教程

    首先声明,本教程的对象是完全没有接触过splay的OIer,大牛请右上角.. 首先引入一下splay的概念,他的中文名是伸展树,意思差不多就是可以随意翻转的二叉树 PS:百度百科中伸展树读作:BoGa ...

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

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

  6. Splay树分析

    简述 Splay树是一种二叉查找平衡树,其又名伸展树,缘由是对其进行任意操作,树的内部结构都会发生类似伸张的动作,换言之,其读和写操作都会修改树的结构.Splay树拥有和其它二叉查找平衡树一致的读写时 ...

  7. splay树入门(带3个例题)

    splay树入门(带3个例题) 首先声明,本教程的对象是完全没有接触过splay的OIer,大牛请右上角.. PS:若代码有误,请尽快与本人联系,我会尽快改正 首先引入一下splay的概念,他的中文名 ...

  8. AVL树、splay树(伸展树)和红黑树比较

    AVL树.splay树(伸展树)和红黑树比较 一.AVL树: 优点:查找.插入和删除,最坏复杂度均为O(logN).实现操作简单 如过是随机插入或者删除,其理论上可以得到O(logN)的复杂度,但是实 ...

  9. Splay树详解

    更好的阅读体验 Splay树 这是一篇宏伟的巨篇 首先介绍BST,也就是所有平衡树的开始,他的China名字是二叉查找树. BST性质简介 给定一棵二叉树,每一个节点有一个权值,命名为 ** 关键码 ...

随机推荐

  1. docker cs50 ide 安装

    ECS上搭建Docker(CentOS7): https://help.aliyun.com/document_detail/51853.html docker官方文档: https://docs.d ...

  2. hdu4149 Magic Potion

    Magic Potion Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Tota ...

  3. springboot入门_模板

    springboot中已经不推荐使用jsp,而是推荐使用模板,如freemarker,thymeleaf等,本文记录在sprigboot中使用模板. 创建一个maven的springboot工程, f ...

  4. Spring-Security自定义登录页&inMemoryAuthentication验证

    Spring Security是为基于Spring的应用程序提供声明式安全保护的安全性框架.框架下内容比较多,可以做到按照角色权限对请求路径进行限制.今天主要验证自定义登录页,在内存用户存储中进行请求 ...

  5. JavaScript那些事

    1.定义静态常量:     const C=1; 该常量不能变化的. 2.在if判断中如果是两个变量比较js会将一个字符和一个数字比较的话,会将字符转换成数字然后在对这两个进行对比: var num= ...

  6. iOS中蓝牙的使用

    Core Bluetooth的使用 1,建立中心设备 2,扫描外设(Discover Peripheral) 3,连接外设(Connect Peripheral) 4,扫描外设中的服务和特征(Disc ...

  7. 轻松搭建Git服务器(Ubuntu)

    搭建Git服务器 在远程仓库节中,我们讲了远程仓库实际上和本地仓库没啥不同,纯粹为了7x24小时开机并交换大家的修改. GitHub就是一个免费托管开源代码的远程仓库.但是对于某些视源代码如生命的商业 ...

  8. 解决maven项目Cannot change version of project facet Dynamic web module to 3.0

    问题描述         用Eclipse创建Maven结构的web项目的时候选择了Artifact Id为maven-artchetype-webapp,由于这个catalog比较老,用的servl ...

  9. 剑指offer第八天

    32.把数组排成最小的数 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323 ...

  10. RGB与HSV之间的转换公式及颜色表

    RGB & HSV 英文全称 RGB - Red, Green, Blue HSV - Hue, Saturation, Value HSV --> RGB 转换公式 HSV --> ...