STL(一)
stl 与高级语法
零、关于我要讲的东西7。3
什么是 stl
标准模板库(Standard Template Library,STL)是惠普实验室开发的一系列软件的统称。它是由Alexander Stepanov、Meng Lee和David R Musser在惠普实验室工作时所开发出来的。虽说它主要出现到C++中,但在被引入C++之前该技术就已经存在了很长时间。STL的代码从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),几乎所有的代码都采用了模板类和模板函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。——百度百科
- algorithm:例如
std::sort - container:例如
std::vector - iterator:等会儿介绍
OI 中 stl 侧重指第二个,我们今天对于三个都要涉及
为什么要讲这个
- 不用背板子的数据结构:
std::priority_queue(堆),std::set(简易平衡树) - 方便代码实现:
std::vector,std::array - 一些奇奇怪怪的用法(比如如何使用
std::vector简易地实现长链剖分) - 防止抱零(百万
deque,@SpadeA261 的vector)
一、对象
struct A{
int a; // 成员
void f(){ // 成员函数
cout<<a<<endl;
}
};
A a;
// A 类型
// a A 类型的对象/示例
花括号初始化器:
A a={1,2};
重载运算符
struct A{
int a;
int b;
bool operator<(A x){
if(a!=x.a) return a<x.a;
return b<x.b;
}
};
// bool operator<(A x,A y){
// if(x.a!=y.a) return x.a<y.a;
// return x.b<y.b;
// }
// bool cmp(A x,A y){
// if(x.a!=y.a) return x.a<y.a;
// return x.b<y.b;
// }
signed main(){
cin.tie(0)->sync_with_stdio(0);
A a,b;
a.operator<(b);
// cmp(a,b);
// operator<(a,b);
}
二、初步介绍容器
序列容器(Sequence Containers)
元素按线性顺序排列,插入位置直接影响存储位置。
如果没有特殊说明,时间复杂度都为 \(O(1)\)(显然不可能是 \(O(1)\) 的另说)
vector:动态数组
.resize().operator[]
只有 \([0,size())\) 内的范围是有效的 @SpadeA261
.push_back()- 构造函数
vector<int> v1(10); // 相当于 (10,1)
vector<int> v2(10,1);
.size().pop_back()
例题:https://www.luogu.com.cn/problem/P5727 使用 vector 的 push_back
insert,erase
\(n^2\) 过万:vector 实现的“平衡树”
- vector 存图:和链式前向星的优劣
- \(n\times m\le 10^6\)
- 扫描线存“事件”
- vector 非常简易实现长链剖分(选讲)
int a[10],n;
vector
signed main(){
cin.tie(0)->sync_with_stdio(0);
sort(a+1,a+1+n); // [1,n]
sort(v.begin(),v.end()); // 所有
sort(v.begin()+1,v.begin()+1+n); // [1,n]
}
array:数组的平替
- 定义
array<int,5> a;
// int a[5];
- 和普通数组一样使用
- .size()
- 数组终于可以用作函数参数/返回值啦!
array<int,2> f(array<int,5> a){
}
void g(int a[5]){
}
- 数组的复制变得简单啦
stack,queue:能不用就不用
stack:不要用,使用 vector 替代
.back(),.pop()
queue:可以使用 vector + 循环替代
关联容器(Associative Containers)
元素自动按键(Key)排序(默认升序),查找复杂度为 O(log n)。底层通常为红黑树。
set,multiset
insert,erase
find,count(注意复杂度陷阱)
lower_bound,upper_bound
在 std::multiset 中,插入与已有元素相同的元素时,新元素会被插入到"等价元素区间"的末尾(即所有相同元素的后面)。
map
insert,erase
count
使用下标访问运算符自动创建元素!
注意多次使用 [] 的常数暴增!善于使用 int& a=mp[x];
无序关联容器(Unordered Associative Containers, C++11)
元素不排序,通过哈希表实现,查找复杂度平均 O(1)(最坏 O(n))。
std::unordered_set 对标 set 无顺序,去重,查找极快。
std::unordered_map 对标 map 最常用键值对,无顺序,查找极快。
容器适配器(Container Adaptors)
它们不是独立的容器,而是对上述容器(默认 deque 或 vector)进行接口封装,提供特定的数据结构行为。
适配器 底层默认容器 特点
std::stack deque 后进先出(LIFO),只能访问栈顶。
std::queue deque 先进先出(FIFO),只能访问队头和队尾。
std::priority_queue vector 优先队列(最大堆),每次弹出最大(或最小)元素。
priority_queue
-
push -
pop -
top -
如何从小到大排:
- 存储数据的相反数
- 其他:
struct Node{ int val; int id; // 方法一:重载比较运算符 // bool operator<(Node x){ // return val<x.val; // } }; /* 方法二:自定义比较 struct CMP{ bool operator()(Node x,Node y){ return x.val<y.val; } }; priority_queue<Node,vector<Node>,CMP> pq; */less,greater
拓展:序关系
比较函数必须是以下几种之一:
- 多关键字比较
- 分别求 result,比 result
- 分数比较
- 自己证明过的正确的比较策略
简单写法:
struct Node{
int a,b,c,d,e;
};
bool operator<(Node a,Node b){
// if(a.a!=b.a) return a.a<b.a;
// if(a.b!=b.b) return a.b<b.b;
// if(a.c!=b.c) return a.c<b.c;
// if(a.d!=b.d) return a.d<b.d;
// return a.e<b.e;
return array<int,5>{a.a,a.b,a.c,a.d,a.e} < array<int,5>{b.a,b.b,b.c,b.d,b.e};
}
basic_string
+=,+
更方便的序列
SSO(Small String Optimization)
basic_string<int> a,b;
a+=1;
a+=2;
a+=3;
a+={4,5};
a+=b;
assert(a==basic_string<int>{1,2,3,4,5});
stl 容器清空:={}
类型:与具体结构体里面的东西无关,不一样的名字就是不一样的类型(除非使用 typedef/using 取别名)
不同类型不能直接赋值
三、程序的内存结构(简化)
这部分是我最想讲的!
很多 OIer 对于程序的内存结构是完全无知的,这在大部分时候其实没什么影响,但是会出现这样的情况:
- 为啥用这个 STL 内存消耗这么大
- MLE 抱零(百万 deque)
为了防止这种事情,我们必须对于程序的内存结构有简单的认识!
指针与地址
这部分内容是 c++ 中必讲的重点,但是 OIer 很多不了解
- 地址
- 数组地址连续
- 内存访问连续的速度快 -> 下标连续访问的那个维度,定义在后面
- 指针
值类别:
- 左值:可以放在等号左边的值
- 右值:不可以放在等号左边的值
可以被取地址的值一定是左值

浙公网安备 33010602011771号