平衡树splay
set,map底层为平衡树
splay,sbt,treap,AVL,替罪羊树
splay:本质为二叉排序树
二叉排序树: 有左子树 < root,柚子树 > root,采用中序遍历时为输出为有序序列
虽然对于二叉树插入and查询为logN,但随着插入不同最坏退化为链O(N)
故此需要平衡的二叉排序树(保证层数)
Tree Rotation操作(核心): 在旋转过程中让树区域平衡

void splay(x) {
    将x节点旋为根
}
即不断将底下的数网上转,旋转为root节点
Splaying的操作(受三种因素影响):
- 节点x是父节点p的左儿子还是右儿子
- 节点p是不是root节点,如果不是
- 节点p是父节点g的左儿子还是右儿子
有三种操作:
Zig step:

当p节点为root节点,进行Zig step,有:
- x为p左孩子:x右旋
- x为p右孩子:x左旋
Zig-Zig step:

当p非root节点,且p和x同为左孩子或者右孩子,有:
- p,x同为左孩子:依次右旋旋转p和x
- p,x同为右孩子:依次左旋旋转p和x
最后将x旋转为root
Zig-Zag step:

当p非root节点,且p和x不同为左孩子或者右孩子,有:
- p为左孩子,x为右孩子:将x左旋再右旋
- p为右孩子,x为左孩子:将x右旋再左旋
对于x和p是不同边儿子时,先将x旋转为p的父亲,再将x旋转为root的位置
即当和父亲为相同儿子:有旋转父亲,再旋转我,和父亲为不同儿子:旋转我,再旋转我

其中记录P:对于x的父亲f的儿子旋转后父子关系不变,与x同向的儿子与x的父子关系同样不改变,变的是与x不同向的儿子,因此进行标记
因此要对p进行记录
板子:
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define MAXN 200005
struct node {
    int data;
}_a[MAXN];
bool operator < (node const &_a, node const &_b) {
    return _a.data < _b.data;   
}
bool operator == (node const &_a, node const &_b) {
    return _a.data == _b.data;   
}
bool operator != (node const &_a, node const &_b) {
    return !(_a.data == _b.data);
}
bool operator > (node const &_a, node const &_b) {
    return !(_a < _b && _a == _b);
}
int _fa[MAXN], _s[MAXN][2], _num[MAXN], _size[MAXN], n, t, _root, _sz;
inline int ws(int x) {//即which son:返回父亲节点儿子是否为自己(1/0)
    return _s[_fa[x]][1] == x;
}
void setson(int son, int f, int w) {//0:左,左小 1:右,大
    //setson:将son接到父亲f,方向为w(儿子的方向)
    if(son != 0) _fa[son] = f;
    if(f != 0) _s[f][w] = son;
}
void maintain(int x) {
    _size[x] = _size[_s[x][0]] + _size[_s[x][1]] + _num[x];
}
void rot(int x) {//旋转x:根据你为左儿子or右儿子,往上旋一层
    int f = _fa[x], ff = _fa[f];//f记录父亲节点是谁,ff为父亲的父亲
    int w = ws(x), wf = ws(f);//w记录我为左儿子还是右儿子,wf为父亲的方向
    int p = _s[x][!w];//p是与自己不同向的儿子
    //记录P:对于x的父亲f的儿子旋转后父子关系不变
    //与x同向的儿子与x的父子关系同样不改变
    //变的是与x不同向的儿子,因此进行标记
    
    
    setson(p, f, w);
    setson(x, ff, wf);//接到爷爷上,方向为父亲方向
    setson(f, x, !w);//将父亲接到自己,方向是我方向的反方向
    
    //maintain维护其他信息:子树大小,子树最大值,子树和等···
    maintain(f);
    maintain(x);
}
void splay(int x) {
    for(; _fa[x]; rot(x)) {//直到root节点都循环进行操作,rot旋自己
        if(_fa[_fa[x]] && ws(_fa[x]) == ws(x)) {//爷爷存在,并且父亲和我方向相同
            rot(_fa[x]);//转父亲,for循环中rot(x)还会保证转一次自己
        }
        _root = x;//记录当前根
    }
}
void insert(int now, node p) {
    if(_root == 0) {
        _root = ++ _sz;
        _a[_sz] = p;
        _size[_sz] = _num[_sz] = 1;
        return ;
    }
    while(_a[now] != p) {
        _size[now] ++ ;
        if(p > _a[now]) {
            if(_s[now][1] == 0) {
                _a[++ _sz] = p;
                setson(_sz, now, 1);
            }
            now = _s[now][1];
        }
        else {
            if(_s[now][0] == 0) {
                -a[++ _sz] = p;
                setson(_sz, now, 0);
            }
            now = _s[now][0];
        }
    }
    _size[now] ++;
    _num[now] ++;
    splay(now);
}
void dfs(int now) {
    if(_s[now][0]) dfs(_s[now][0]);
    for(int i = 1; i <= _num[now]; ++ i) {
        cut << _num[now] << " ";
    }
    if(_s[now][1]) dfs(_s[now][1]);
}
int main() {
    freopen("in.txt", "r". stdin);
    freopen("splay.txt", "w", stdout);
    int C;
    scanf("%d", &C);
    while(C -- ) {
        
    }
    return 0;
}
对于删除操作:splay树支持删除操作,先将删除节点旋转到root节点,断开左右子树:
- 断柚子树:找左子树最大的点
- 断左子树:找柚子树最小的点
采用结构体记录或者遍历的方式找到,然后将左右子树(最大/最小点p)splay为root节点,此时显然p无右/左儿子,setson将另一边接上去即可

 
                     
                    
                 
                    
                 
 
         
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号