rentenglong大佬写的splay的梳理使我受益颇丰,特此做出一定的总结。
数据结构
定义了一个struct结构体。
为了在splay操作下储存根节点,我们宏定义了root 为 tree[0].ch[1],则我们把tree[0]看作了整棵树的超级根,而tree[0]的右儿子为排序树的根节点,那么一定有tree[0].v < tree[tree[0].ch[1]].v,所以我们需要将tree[0].v赋值为无穷小。
struct Node{ int v, father; //v 保存的值, fa 该点的父亲结点 int ch[2]; //ch[0] 左孩子(小于根) ch[1] 右孩子(大于根) int sum; //以该结点为子树的所有数的出现次数 int recy; //该结点所代表的数的出现次数(记录重复次数) }tree[MAX];
另外我们用两个变量n(搜索树的结点个数),points(整棵树中所有数的储存次数)。
更新当前结点的sum值
在进行splay操作后整棵树的结构可能会发生改变,那么有些结点的sum值就有可能会发生改变,我们需要维护sum的原有定义,所以我们要把当前结点的sum给更新一下。
void updata(int x) { tree[x].sum = tree[tree[x].ch[0]].sum + tree[tree[x].ch[1]].sum + tree[x].recy; }
获取父子关系
用于确定当前结点是父亲结点的左孩子(ch[0])还是右孩子(ch[1])。左孩子小于父亲结点,右孩子大于父亲结点。
int identify(int x) { return tree[tree[x].father].ch[0] == x ? 0 : 1; }
建立父子关系
用于连接两个点,其中一个点是另一个点的孩子。connect(x, f, son(0/1))表示 x 是 f 的 son(0/1) 孩子(0表示左,1表示右)。
void connect(int x, int f, int son) { tree[x].father = f; tree[f].ch[son] = x; }
旋转操作
所谓旋转指的是把指定结点向上移动一级然后将其父亲结点作为他的儿子。
通过异或操作可以进行。
void rotate(int x) { int y = tree[x].father; int mroot = tree[y].father; int mrootson = identify(y); int yson = identify(x); int b = tree[x].ch[yson ^ 1]; connect(b, y, yson), connect(y, x, (yson ^ 1)), connect(x, mroot, mrootson); updata(y), updata(x); }
伸展操作
把我们要用的结点旋转到根上来。
考虑双旋和单旋,节省时间。
void splay(int at, int to) { to = tree[to].father; for(; tree[at].father != to;) { int up = tree[at].father; if(tree[up].father == to) rotate(at); else if(identify(up) == identify(at)) rotate(up), rotate(at); else rotate(at), rotate(at); } }
新建结点&破坏处理废物结点
int crepoint(int v, int father) { n ++; tree[n].v = v; tree[n].father = father; tree[n].sum = tree[n].recy = 1; return n; } void destory(int x) { tree[x].v = tree[x].ch[0] = tree[x].ch[1] = tree[x].father = tree[x].recy = tree[x].sum = 0; if(x == n) n --; }
内置的插入结点
int build(int v) { points ++; if(n == 0) { root = 1; crepoint(v, 0); } else { int now = root; for(;;) { if(now) tree[now].sum ++; if(v == tree[now].v) { tree[now].recy ++; return now; } int Next = v < tree[now].v ? 0 : 1; if(!tree[now].ch[Next]) { crepoint(v, now); tree[now].ch[Next] = n; return n; } now = tree[now].ch[Next]; } } return 0; }
外部调用的查找结点
int find(int v) { int now = root; for(;;) { if(tree[now].v == v) { splay(now, root); return now; } int Next = v < tree[now].v ? 0 : 1; if(!tree[now].ch[Next]) return 0; now = tree[now].ch[Next]; } }
外部调用的插入结点&删除结点
void Push(int v) { int add = build(v); splay(add, root); } void Pop(int v) { int deal = find(v); if(!deal) return; points --; if(tree[deal].recy > 1) { tree[deal].recy --; tree[deal].sum --; return; } if(!tree[deal].ch[0]) { root = tree[deal].ch[1]; tree[root].father = 0; } else { int Left = tree[deal].ch[0]; for(; tree[Left].ch[1];) Left = tree[Left].ch[1]; splay(Left, tree[deal].ch[0]); int Right = tree[deal].ch[1]; connect(Right, Left, 1), connect(Left, 0, 1); updata(Left); } destory(deal); }
查询第v大&查询v是第几大
int Rank(int v)//查询v的排名 { int ans = 0, now = root; for(;;) { if(tree[now].v == v) return ans + tree[tree[now].ch[0]].sum + 1; if(now == 0) return 0; if(v < tree[now].v) now = tree[now].ch[0]; else{ ans = ans + tree[tree[now].ch[0]].sum + tree[now].recy; now = tree[now].ch[1]; } } if(now) splay(now, root); return 0; } int At_rank(int x)//查询排名为x的数 { if(x > points) return -INF; int now = root; for(;;) { int minused = tree[now].sum - tree[tree[now].ch[1]].sum; if(x > tree[tree[now].ch[0]].sum && x <= minused) break; if(x < minused) now = tree[now].ch[0]; else{ x = x - minused; now = tree[now].ch[1]; } } splay(now, root); return tree[now].v; }
查找前驱&后继
int Upper(int v) { int now = root; int result = INF; for(; now;) { if(tree[now].v > v && tree[now].v < result) result = tree[now].v; if(v < tree[now].v) now = tree[now].ch[0]; else now = tree[now].ch[1]; } return result; } int Lower(int v) { int now = root; int result = -INF; for(; now;) { if(tree[now].v < v && tree[now].v > result) result = tree[now].v; if(v > tree[now].v) now = tree[now].ch[1]; else now = tree[now].ch[0]; } return result; }
整段代码(洛谷P3369)
#include<bits/stdc++.h> using namespace std; const int MAX = 1000005; const int INF = 214780000; class Splay { #define root tree[0].ch[1] private: struct Node{ int v, father; //v 保存的值, fa 该点的父亲结点 int ch[2]; //ch[0] 左孩子(小于根) ch[1] 右孩子(大于根) int sum; //以该结点为子树的所有数的出现次数 int recy; //该结点所代表的数的出现次数(记录重复次数) }tree[MAX]; int n = 0, points = 0; void updata(int x) { tree[x].sum = tree[tree[x].ch[0]].sum + tree[tree[x].ch[1]].sum + tree[x].recy; } int identify(int x) { return tree[tree[x].father].ch[0] == x ? 0 : 1; } void connect(int x, int f, int son) { tree[x].father = f; tree[f].ch[son] = x; } void rotate(int x) { int y = tree[x].father; int mroot = tree[y].father; int mrootson = identify(y); int yson = identify(x); int b = tree[x].ch[yson ^ 1]; connect(b, y, yson), connect(y, x, (yson ^ 1)), connect(x, mroot, mrootson); updata(y), updata(x); } void splay(int at, int to) { to = tree[to].father; for(; tree[at].father != to;) { int up = tree[at].father; if(tree[up].father == to) rotate(at); else if(identify(up) == identify(at)) rotate(up), rotate(at); else rotate(at), rotate(at); } } int crepoint(int v, int father) { n ++; tree[n].v = v; tree[n].father = father; tree[n].sum = tree[n].recy = 1; return n; } void destory(int x) { tree[x].v = tree[x].ch[0] = tree[x].ch[1] = tree[x].father = tree[x].recy = tree[x].sum = 0; if(x == n) n --; } int build(int v) { points ++; if(n == 0) { root = 1; crepoint(v, 0); } else { int now = root; for(;;) { if(now) tree[now].sum ++; if(v == tree[now].v) { tree[now].recy ++; return now; } int Next = v < tree[now].v ? 0 : 1; if(!tree[now].ch[Next]) { crepoint(v, now); tree[now].ch[Next] = n; return n; } now = tree[now].ch[Next]; } } return 0; } public : int getroot() {return root;} void init() { tree[0].v = -INF; } int find(int v) { int now = root; for(;;) { if(tree[now].v == v) { splay(now, root); return now; } int Next = v < tree[now].v ? 0 : 1; if(!tree[now].ch[Next]) return 0; now = tree[now].ch[Next]; } } void Push(int v) { int add = build(v); splay(add, root); } void Pop(int v) { int deal = find(v); if(!deal) return; points --; if(tree[deal].recy > 1) { tree[deal].recy --; tree[deal].sum --; return; } if(!tree[deal].ch[0]) { root = tree[deal].ch[1]; tree[root].father = 0; } else { int Left = tree[deal].ch[0]; for(; tree[Left].ch[1];) Left = tree[Left].ch[1]; splay(Left, tree[deal].ch[0]); int Right = tree[deal].ch[1]; connect(Right, Left, 1), connect(Left, 0, 1); updata(Left); } destory(deal); } int Rank(int v) { int ans = 0, now = root; for(;;) { if(tree[now].v == v) return ans + tree[tree[now].ch[0]].sum + 1; if(now == 0) return 0; if(v < tree[now].v) now = tree[now].ch[0]; else{ ans = ans + tree[tree[now].ch[0]].sum + tree[now].recy; now = tree[now].ch[1]; } } if(now) splay(now, root); return 0; } int At_rank(int x) { if(x > points) return -INF; int now = root; for(;;) { int minused = tree[now].sum - tree[tree[now].ch[1]].sum; if(x > tree[tree[now].ch[0]].sum && x <= minused) break; if(x < minused) now = tree[now].ch[0]; else{ x = x - minused; now = tree[now].ch[1]; } } splay(now, root); return tree[now].v; } int Upper(int v) { int now = root; int result = INF; for(; now;) { if(tree[now].v > v && tree[now].v < result) result = tree[now].v; if(v < tree[now].v) now = tree[now].ch[0]; else now = tree[now].ch[1]; } return result; } int Lower(int v) { int now = root; int result = -INF; for(; now;) { if(tree[now].v < v && tree[now].v > result) result = tree[now].v; if(v > tree[now].v) now = tree[now].ch[1]; else now = tree[now].ch[0]; } return result; } #undef root }; Splay F; inline int read() { int x = 0, w = 0; char ch= getchar(); for(; !isdigit(ch); w |= (ch == '-'), ch = getchar()); for(; isdigit(ch); x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar()); return w ? -x : x; } int main() { int opt, n, x; //scanf("%d", &n); n = read(); F.init(); for(int i = 1; i <= n; ++ i) { //scanf("%d%d", &opt, &x); opt = read(), x = read(); switch(opt) { case 1: F.Push(x); break; case 2: F.Pop(x); break; case 3: printf("%d ", F.Rank(x)); break; case 4: printf("%d ", F.At_rank(x)); break; case 5: printf("%d ", F.Lower(x)); break; case 6: printf("%d ", F.Upper((x))); break; } } return 0; }