什么时候左旋右旋

当对树的形态进行操作的时候 才会进行zig/zag 各种查询操作均用不上
- 插入的时候判断一下新增的节点的方向上 是否需要maintain
- 删除的时候通过旋转 把要删除的节点旋转至 叶子 然后删除
- 右旋先对右儿子进行pushup 再对父亲做
- 新增节点 删除节点 以及左旋右旋后 pushup
常见issues
见代码注释
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#include<ctime>using namespace std;// #define int long longconst int maxn = 1e6 + 10;const int INF = 0x7fffffff;struct node{int l, r;int val, dat;int cnt, siz;}treap[maxn << 2];int tot, root , n;int New(int val){treap[++tot].val = val;treap[tot].dat = rand();treap[tot].cnt = treap[tot].siz = 1;return tot;}void pushup(int x){treap[x].siz = treap[treap[x].l].siz + treap[treap[x].r].siz + treap[x].cnt;}void build(){/*在找前驱和后继的时候防止找到空节点*/New(-INF);New(INF);root = 1;treap[1].r = 2;pushup(root);}/*右旋, 本来的父亲节点成为了自己左儿子的右儿子*/void zig(int &x){int y = treap[x].l;treap[x].l = treap[y].r; //原本父亲节点的左儿子变成了 他左儿子的右儿子treap[y].r = x; // 左儿子的右儿子 变成了原本的父亲节点x = y;pushup(treap[x].r);pushup(x);}/*左旋, 本来的父亲节点成为了自己右儿子的左儿子*/void zag(int &x){int y = treap[x].r;treap[x].r = treap[y].l;treap[y].l = x;x = y;pushup(treap[x].l);pushup(x);}void insert(int &x, int val){if(x == 0){x = New(val);return;}if(val == treap[x].val){ //已经有的值增加计数即可treap[x].cnt++;pushup(x);return;}if(val < treap[x].val){insert(treap[x].l, val);if(treap[x].dat < treap[treap[x].l].dat){zig(x);}} else{insert(treap[x].r, val);if(treap[x].dat < treap[treap[x].r].dat){zag(x);}}pushup(x);}int getPre(int val){int ans = 1;int x = root;while(x){if(val == treap[x].val){if(treap[x].l > 0){x = treap[x].l;while(treap[x].r > 0) x = treap[x].r; //往左子树中的右边一直找ans = x;}break;}if(treap[x].val < val&&treap[x].val > treap[ans].val) ans = x;//可以作为前驱的答案 比当前答案更优x = val < treap[x].val? treap[x].l : treap[x].r;}return treap[ans].val;}int getNxt(int val){int ans = 2;int x = root;while(x){if(val == treap[x].val){if(treap[x].r > 0){x = treap[x].r;while(treap[x].l > 0) x = treap[x].l;ans = x;}break;}if(treap[x].val > val&&treap[x].val < treap[ans].val) ans = x;x = val < treap[x].val? treap[x].l:treap[x].r;}return treap[ans].val;}void remove(int &x, int val){if(x == 0) return;if(val == treap[x].val){if(treap[x].cnt > 1){treap[x].cnt--;pushup(x);return;}if(treap[x].l || treap[x].r){if(treap[x].r == 0|| treap[treap[x].l].dat > treap[treap[x].r].dat){zig(x);remove(treap[x].r, val);} else{zag(x);remove(treap[x].l, val);}pushup(x);} else{x = 0;}return;}val < treap[x].val? remove(treap[x].l, val):remove(treap[x].r, val);pushup(x);}int getRankByVal(int x, int val){if(x == 0) return 0;if(val == treap[x].val){return treap[treap[x].l].siz + 1; // 比他小的数+1 即得到排名}if(val < treap[x].val){return getRankByVal(treap[x].l, val); // 比当前值还要小 继续往左子树找} else{/*左边的小的 全部算上 当前节点也比你要找的值小 有多少副本数全部算上*/return treap[treap[x].l].siz + getRankByVal(treap[x].r, val) + treap[x].cnt;}}int getValByRank(int x, int rank){if(x == 0) return INF;if(treap[treap[x].l].siz >= rank){//比你小的数 还有很多 还要继续往左边找return getValByRank(treap[x].l, rank);} else if((treap[treap[x].l].siz + treap[x].cnt) >= rank){//本身的副本数刚好覆盖掉了 就是当前的值return treap[x].val;} else{//继续往比当前值大的区域寻找 要找的名词减去当前点以及它左子树的贡献return getValByRank(treap[x].r, rank - treap[treap[x].l].siz - treap[x].cnt);}}int main(){// freopen("out.txt", "w", stdout);build();int op, x;scanf("%d", &n);for(int i = 0; i < n; ++i){scanf("%d%d", &op, &x);switch(op){case 1: //插入insert(root, x);break;case 2: //删除remove(root, x);break;case 3: //按值查询 排名printf("%d\n", getRankByVal(root, x) - 1);break;case 4: //按排名查询值printf("%d\n", getValByRank(root, x + 1));break;case 5: //前驱printf("%d\n", getPre(x));break;case 6: //后继printf("%d\n", getNxt(x));break;}}return 0;}
