平衡树学习笔记(1) BST

BST,二叉排序树。这个东西应该算是联赛算法?然而我一直没有学orz。
于是我就调了一天。。真菜啊我
BST的原理很简单,保证中序遍历是有序的,我们称之为BST性质。

存储

为了方便之后的操作,这里我们用结构体来实现

struct node{
    int son[2], fa, size, val; //son[0]为左儿子,son[1] 为右儿子
}a[100003];

遍历

inline void go(int k) {
    if(a[k].son[0]) go(a[k].son[0]);
    cout<<a[k].val<<" ";
    if(a[k].son[1]) go(a[k].son[1]);
}

插入元素

然后考虑插入。实现起来也很简单,递归的一顿乱搞就可以。

inline void insert(int x, int k) {
    if(cnt == 1) {
        a[1].val = x;
        a[1].size = 1;
        a[1].fa = nil;
        ++cnt;
        return;
    } 
    if(x < a[k].val) {
        if(!a[k].son[0]) {
            a[k].son[0] = cnt;
            a[cnt].fa = k;
            a[cnt].val = x;
            a[cnt].size = 1;
            ++cnt;
        } else insert(x, a[k].son[0]);
    } else {
        if(!a[k].son[1]) {
            a[k].son[1] = cnt;
            a[cnt].fa = k;
            a[cnt].val = x;
            a[cnt].size = 1;
            ++cnt;
        } else insert(x, a[k].son[1]);
    }
    update(k);
}

其中update操作用来更新大小

inline void update(int k) {
    a[k].size = a[a[k].son[0]].size + a[a[k].son[1]].size + 1;
}

然后拿着这个模板可以去试着提交一下luogu1177快速排序,会T三个点,如果random_shuffle一下T两个。
姑且不论这些玄学问题,我们来看看其他操作。

查找一个数的位置

许多操作的基础。
同样递归的查找即可。

inline int find(int x, int k) {
    if(a[k].val == x) return k;
    if(x < a[k].val && a[k].son[0]) 
        return find(x, a[k].son[0]);
    if(x > a[k].val && a[k].son[1])
        return find(x, a[k].son[1]);
    return nil;
}

前驱

前驱就是中序遍历结果的前一个元素。
我们分类讨论一下:
如果有左子树,那么就是左子树的最大的元素也就是最右边的元素
如果没有左子树,是父亲的右子树,那么前驱是父亲。
如果没有左子树,是父亲的左子树,那么前驱就是一直找父亲直到某一个父亲是父亲的右子树。
image_1c20qtmia1jen138fdc1qhtondm.png-12.1kB

放张图解释一下好了。
B的前驱是D
H的前驱是F,而E的前驱是B
G的前驱则是E,寻找过程是F->E为右子树

inline int prenode(int x) {
    int k = find(x, 0);
    if(k == nil) return nil;
    if(a[k].son[0]) {
        k = a[k].son[0];
        while(a[k].son[1])
            k = a[k].son[1];
        return a[k].val;
    }
    while(a[k].fa != nil) {
        if(a[a[k].fa].son[1] == k) 
            return a[a[k].fa].val;
        k = a[k].fa; 
    }
    return nil;
}

后继

后继就是中序遍历结果的后一个元素。
我们分类讨论一下:
如果有右子树,那么后继就是右子树最小的元素也就是最左的元素
如果没有右子树,是父亲的左子树,那么后继就是父亲
如果没有右子树,是父亲的右子树,那么后继就是一直找父亲直到某一个父亲是父亲的左子树。

inline int nxtnode(int x) {
    int k = find(x, 1);
    if(k == nil) return nil;
    if(a[k].son[1]) {
        k = a[k].son[1];
        while(a[k].son[0]){
            k = a[k].son[0];
        } 
        return a[k].val;
    }
    while(a[k].fa != nil) {
        if(a[a[k].fa].son[0] == k)
            return a[a[k].fa].val;
        k = a[k].fa; 
    }
    return nil;
}

排名

排名指的是第几小(大),根据子树性质递归即可

inline int getrank(int x, int k, int ct) {
    if(a[k].val == x) return (ct + a[a[k].son[0]].size + 1);
    else if(a[k].son[0] && a[k].val > x) 
        return getrank(x, a[k].son[0], ct);
    else if(a[k].son[1] && a[k].val < x) 
        return getrank(x, a[k].son[1], ct + a[a[k].son[0]].size + 1);
    return nil;
} 

k大

查找第k大的元素,如果在左子树就直接查找,否则对右子树查找k - a[a[k].son[0]].size - 1的元素

inline int getkth(int x, int k) {
    if(x == a[a[k].son[0]].size + 1)
        return a[k].val;
    else if(a[k].son[0] && x <= a[a[k].son[0]].size) 
        return getkth(x, a[k].son[0]);
    else if(a[k].son[1] && x >= a[a[k].son[0]].size + 2)
        return getkth(x - a[a[k].son[0]].size - 1, a[k].son[1]);
}

删除

删除可能是最刺激的一个操作。因为二叉排序树的性质往往有牵一发而动全身的特性,所以(你懂得)
我们需要一个函数clear:

inline void clear(int k) {
    a[k].son[0] = a[k].son[1] = a[k].fa = a[k].size = a[k].val = 0;
}

首先,如果左右儿子都没有,那就把父亲的儿子删掉之后直接clear掉。
其次,如果只有一个儿子,就嫡长子继承,把儿子搞上来即可。
最后,如果两个儿子,就找到后继。由于已经事先剔除了没有儿子的情况,所以后继人一定在儿子中,所以我们可以直接交换两者,并且对后继进行删除操作。

inline int erase(int x, int k) {
    if(!a[k].son[0] && !a[k].son[1]) {
        a[a[k].fa].son[a[a[k].fa].son[1] == k] = 0;
        for(int i = a[k].fa; i != nil; i = a[i].fa)
            --a[i].size;
        clear(k);
    }
    else if(!a[k].son[0] || !a[k].son[1]) {
        for(int i = a[k].fa; i != nil; i = a[i].fa)
            --a[i].size;
        int wson = (a[k].son[0] == 0), wk = a[k].son[wson];
        a[a[k].fa].son[a[a[k].fa].son[1] == k] = wk;
        a[a[k].son[wson]].fa = a[k].fa;
        clear(k);
    } else {
        int tmpk = nxtnode(x);
        a[k].val = a[tmpk].val;
        erase(a[tmpk].val, tmpk);
    }
}

完整代码

#include <bits/stdc++.h>
using namespace std;
#define nil 0x3fffff
int cnt = 1;
int qwq[100003]; 
struct node{
    int son[2], fa, size, val;
}a[100003];
inline void read(int &x) {
    x = 0; char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
inline void update(int k) {
    a[k].size = a[a[k].son[0]].size + a[a[k].son[1]].size + 1;
}
inline void insert(int x, int k) {
    if(cnt == 1) {
        a[1].val = x;
        a[1].size = 1;
        a[1].fa = nil;
        ++cnt;
        return;
    } 
    if(x < a[k].val) {
        if(!a[k].son[0]) {
            a[k].son[0] = cnt;
            a[cnt].fa = k;
            a[cnt].val = x;
            a[cnt].size = 1;
            ++cnt;
        } else insert(x, a[k].son[0]);
    } else {
        if(!a[k].son[1]) {
            a[k].son[1] = cnt;
            a[cnt].fa = k;
            a[cnt].val = x;
            a[cnt].size = 1;
            ++cnt;
        } else insert(x, a[k].son[1]);
    }
    update(k);
}
inline void go(int k) {
    if(a[k].son[0]) go(a[k].son[0]);
    cout<<a[k].val<<" ";
    if(a[k].son[1]) go(a[k].son[1]);
}
inline int find(int x, int k) {
    if(a[k].val == x) return k;
    if(x < a[k].val && a[k].son[0]) 
        return find(x, a[k].son[0]);
    if(x > a[k].val && a[k].son[1])
        return find(x, a[k].son[1]);
    return nil;
}
inline int prenode(int x) {
    int k = find(x, 1);
    if(k == nil) return nil;
    if(a[k].son[0]) {
        k = a[k].son[0];
        while(a[k].son[1])
            k = a[k].son[1];
        return k;
    }
    while(a[k].fa != nil) {
        if(a[a[k].fa].son[1] == k) 
            return a[k].fa;
        k = a[k].fa; 
    }
    return nil;
}
inline int nxtnode(int x) {
    int k = find(x, 1);
    if(k == nil) return nil;
    if(a[k].son[1]) {
        k = a[k].son[1];
        while(a[k].son[0]){
            k = a[k].son[0];
        } 
        return k;
    }
    while(a[k].fa != nil) {
        if(a[a[k].fa].son[0] == k)
            return a[k].fa;
        k = a[k].fa; 
    }
    return nil;
}
inline int getrank(int x, int k, int ct) {
    if(a[k].val == x) return (ct + a[a[k].son[0]].size + 1);
    else if(a[k].son[0] && a[k].val > x) 
        return getrank(x, a[k].son[0], ct);
    else if(a[k].son[1] && a[k].val < x) 
        return getrank(x, a[k].son[1], ct + a[a[k].son[0]].size + 1);
    return nil;
} 
inline int getkth(int x, int k) {
    if(x == a[a[k].son[0]].size + 1)
        return a[k].val;
    else if(a[k].son[0] && x <= a[a[k].son[0]].size) 
        return getkth(x, a[k].son[0]);
    else if(a[k].son[1] && x >= a[a[k].son[0]].size + 2)
        return getkth(x - a[a[k].son[0]].size - 1, a[k].son[1]);
}
inline void clear(int k) {
    a[k].son[0] = a[k].son[1] = a[k].fa = a[k].size = a[k].val = 0;
}
inline int erase(int x, int k) {
    if(!a[k].son[0] && !a[k].son[1]) {
        a[a[k].fa].son[a[a[k].fa].son[1] == k] = 0;
        for(int i = a[k].fa; i != nil; i = a[i].fa)
            --a[i].size;
        clear(k);
    }
    else if(!a[k].son[0] || !a[k].son[1]) {
        for(int i = a[k].fa; i != nil; i = a[i].fa)
            --a[i].size;
        int wson = (a[k].son[0] == 0), wk = a[k].son[wson];
        a[a[k].fa].son[a[a[k].fa].son[1] == k] = wk;
        a[a[k].son[wson]].fa = a[k].fa;
        clear(k);
    } else {
        int tmpk = nxtnode(x);
        a[k].val = a[tmpk].val;
        erase(a[tmpk].val, tmpk);
    }
}
int main() {
    int n;
    read(n);
    int tmp;
    for(int i = 0;  i < n; ++i) {
        read(qwq[i]);
    }
    for(int i = 0; i < n; ++i) insert(qwq[i], 1);
    //go(1);
    //cout<<endl;
    //for(int i = 1; i < cnt; ++i) cout<<a[i].size<<" ";
    //cout<<endl;
    while(1) {
        int cmd;
        cin>>cmd;
        int x;
        if(cmd == 1) read(x), insert(x, 1);
        if(cmd == 2) go(1), cout<<endl;
        if(cmd == 3) read(x), cout<<find(x, 1)<<endl;
        if(cmd == 4) read(x), cout<<a[prenode(x)].val<<endl;
        if(cmd == 5) read(x), cout<<a[nxtnode(x)].val<<endl;
        if(cmd == 6) read(x), cout<<getrank(x, 1, 0)<<endl;
        if(cmd == 7) read(x), cout<<getkth(x, 1);
        if(cmd == 8) read(x), erase(x, find(x, 1));
        if(cmd == 9) break;
    }
    return 0;
} 
/*
7
3 5 7 4 1 2 8
7
1 5 6 2 3 7 4
*/

怎么这么菜啊我

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值