如何优雅的用vector切掉平衡树??
vector作为一个强大的容器,模拟平衡树也是不在话下
来看一道例题:
P3369 【模板】普通平衡树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
【模板】普通平衡树
题目描述
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
- 插入 \(x\) 数
- 删除 \(x\) 数(若有多个相同的数,因只删除一个)
- 查询 \(x\) 数的排名(排名定义为比当前数小的数的个数 \(+1\) )
- 查询排名为 \(x\) 的数
- 求 \(x\) 的前驱(前驱定义为小于 \(x\),且最大的数)
- 求 \(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也能过