如何优雅的用vector切掉平衡树??

vector作为一个强大的容器,模拟平衡树也是不在话下

来看一道例题:

P3369 【模板】普通平衡树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

【模板】普通平衡树

题目描述

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  1. 插入 \(x\)
  2. 删除 \(x\) 数(若有多个相同的数,因只删除一个)
  3. 查询 \(x\) 数的排名(排名定义为比当前数小的数的个数 \(+1\) )
  4. 查询排名为 \(x\) 的数
  5. \(x\) 的前驱(前驱定义为小于 \(x\),且最大的数)
  6. \(x\) 的后继(后继定义为大于 \(x\),且最小的数)

输入格式

第一行为 \(n\),表示操作的个数,下面 \(n\) 行每行有两个数 \(\text{opt}\)\(x\)\(\text{opt}\) 表示操作的序号( $ 1 \leq \text{opt} \leq 6 $ )

输出格式

对于操作 \(3,4,5,6\) 每行输出一个数,表示对应答案

样例 #1

样例输入 #1

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

样例输出 #1

106465
84185
492737

提示

【数据范围】
对于100%的数据,1<=n<=105$,|x|<=107

vector模拟平衡树主要为以下几个操作:

1.插入

用insert函数实现,insert(k,x);指在下标为k的地方插入x,注意k为指针,所以实际插入在某个下标时可以k+vec.begin()

其中vec.begin()返回vector起始位置的迭代器,所以加上就成指针型的了

好了晕针的朋友先别跑,其实实现起来和指针没太大关系

但是我们要按照平衡树的那种顺序插入,也就是在插入x时,要保证左边比他小,右边比他大

没错我们直接二分查找插入

vector的二分非常方便的一点就是lower_bound返回的就是迭代器,可以直接对应vector下标

vec.insert(lower_bound(vec.begin(),vec.end(),x),x);

insert函数会将原位上包括后面的值向后移一位,所以复杂度是O(n)的,这也是vector慢的主要原因之一

2.删除:

用erase函数实现,erase(x)指把下标为x的数删除,后边的数补过来,x也是个指针

同样的我们也可以二分查找

3.排名:

查找到x的下标,他前面有多少个数就是多少名+1

4.第k大的数:

就是vector中的第k数vec[k-1],因为vector是从0开始存的

5.前驱,后继:

考虑到有重复数值,前驱就是第一个小于等于它的数的前一个数,后继就是第一个严格大于他的数

直接lower_bound和upper_bound

完整代码:

#include <bits/stdc++.h>
using namespace std;
const int M = 1e6+100;
vector <int> vec;
inline int read(){
    int x=0,f=0;char c=getchar();
    while(!isdigit(c)){
        if(c=='-') f=1;
        c=getchar();
    }
    do{
        x=(x<<3)+(x<<1)+(c^48);
    }while(isdigit(c=getchar()));
    return f?-x:x;
}
inline void print(int x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) print(x/10);putchar(x%10+'0');
}
int main(){
     int n=read();
    for(int i=1;i<=n;i++){
        int opt=read(),x=read();
        if(opt==1)    vec.insert(lb(vec.begin(),vec.end(),x),x);
        if(opt==2)    vec.erase (lb(vec.begin(),vec.end(),x));
        if(opt==3)    printf("%d\n",lb(vec.begin(),vec.end(),x)-vec.begin()+1);
        if(opt==4)    printf("%d\n",vec[x-1]);
        if(opt==5)    printf("%d\n",vec[lb(vec.begin(),vec.end(),x)-vec.begin()-1]);
        if(opt==6)    printf("%d\n",vec[upper_bound(vec.begin(),vec.end(),x)-vec.begin()]);
    }      
    return 0;
}

开了O2效率还是很不错的,实际上不开O2也能过

posted @ 2022-06-08 15:11  blue_tsg  阅读(83)  评论(0编辑  收藏  举报