本文介绍了什么是 STL 以及如何使用 STL 更高效
偷懒地解题。本篇文章将会长期更新,欢迎大家一起监督学习。
1. STL概念
STL(Standard Template Library,标准模板库),是惠普实验室开发的一系列软件的统称。现主要出现在 C++ 中,STL 从广义上分为:容器(Container)、算法(Algorithm)和迭代器(Iterator)。STL 几乎所有的代码都采用了模板类或者模板函数,这相比传统的由函数和类组成的库来说提供了更好的代码重用机会。
2. STL六大组件
STL 提供了六大组件,彼此之间可以组合套用,这六大组件分别是容器、算法、迭代器、仿函数、适配器、空间配置器。其中,在算法竞赛中用到最多的为容器、算法与迭代器。
- 容器(
Container):STL 容器为各种数据结构,如vector、stack、queue、map、set等,用来存放数据,从实现角度来看,STL 容器是一种class template。 - 算法(
Algorithm):STL 的算法多数定义在<algorithm>头文件中,其中包括了各种常用的算法,如sort、find、copy、reverse等,从实现角度来看,STL 算法是一种function template。 - 迭代器(
Iterator):STL 迭代器扮演了容器与算法之间的胶合剂,共有五种类型,从实现角度来看,迭代器是一种将opetator*、opetator->、operator++等指针相关操作予以重载的class template。所有 STL 容器都附带有自己专属的迭代器,只有容器的设计者才知道如何遍历自己的元素。 - 仿函数(
Functor):行为类似函数,可作为算法的某种策略,从实现角度来看,仿函数是一种重载了operator()的class或者class template。 - 适配器(
Adaptor):一种用来修饰容器或仿函数或迭代器接口的东西。 - 空间配置器(
Allocator):负责空间的配置与管理。从实现角度来看,配置器是一个实现了动态空间配置、空间管理、空间释放的class template。
3. STL容器
相信很多人学习 STL 就是为了在比赛中能够更好地装B运用各种数据结构和算法,提高解题速度。确实,使用 STL 中的容器能够不需要自己手写定义各种数据结构,使用 STL 中的算法能够不需要自己手写实现各种基本算法,因此本部分对于算法巨巨们是最为重要的一部分,那么 STL 容器究竟有哪些呢?在做题中该如何使用呢?
3.1 vector
vector 又称变长数组,定义在 <vector> 头文件中,vector 容器是动态空间,随着元素的加入,它的内部机制会自动扩充空间以容纳新的元素。因此 vector 的运用对于内存的合理利用与运用的灵活性有很大的帮助。
vector的定义方式:
1 | vector<int> v; // 定义一个vector,其中的元素为int类型 |
vector的常用内置函数:
1 | // vector中的常用内置函数 |
3.2 stack
stack 又称栈,是一种后进先出(Last In First Out,LIFO)的数据结构,定义在 <stack> 头文件中,stack 容器允许新增元素、移除元素、取得栈顶元素,但是除了最顶端以外,没有任何方法可以存取 stack 的其它元素,换言之,stack 不允许有遍历行为。
stack的定义方式:
1 | stack<int> stk; // 定义一个stack,其中元素的类型为int |
stack的常用内置函数:
1 | // stack中的常用内置函数 |
3.3 string
string 又称字符串,定义在 <string> 头文件中。C 风格的字符串(以空字符结尾的字符数组)太过复杂难于掌握,因此 C++ 标准库定义了一种 string 类。string 和 vector<char> 在数据结构、内存管理等方面都是相同的。但是,vector<char> 只是单纯的一个“char 元素的容器”,而 string 不仅是一个“char 元素的容器”,它还扩展了一些针对字符串的操作,例如 string 可以使用 c_str() 函数转换为 C 风格的字符串,vector 中并未对输入输出流操作符进行重载,因此无法直接对 vector<char> 进行 cin 或者 cout 这样的操作,但是 string 可以,且 vector<char> 并不能直接实现字符串的拼接,但是 string 可以,string 中重载了 +, += 运算符。
string的定义方式:
1 | string str; // 定义一个空的字符串 |
string的常用内置函数:
1 | // string中的常用内置函数 |
string的erase()与remove()函数的用法:
1 | // string中erase()与remove()的用法 |
3.4 queue/priority_queue
queue 又称队列,是一种先进先出(First In First Out,FIFO)的数据结构,定义在 <queue> 头文件中,queue 容器允许从一端(称为队尾)新增元素(入队),从另一端(称为队头)移除元素(出队)。
priority_queue 又称优先队列,同样定义在 <queue> 头文件中,与 queue 不同的地方在于我们可以自定义其中数据的优先级,优先级高的排在队列前面,优先出队。priority_queue 具有 queue 的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它的本质是用堆实现的,因此可分为小根堆与大根堆,小根堆中较小的元素排在前面,大根堆中较大的元素排在前面。(创建 priority_queue 时默认是大根堆!)
queue/priority_queue的定义方式:
1 | queue<int> que; // 定义一个queue,其中元素的类型为int |
queue/priority_queue的常用内置函数:
1 | // queue/priority_queue中的常用内置函数 |
3.5 deque
deque 又称双端队列,定义在 <deque> 头文件中,vector 容器是单向开口的连续内存空间,deque 则是一种双向开口的连续线性空间。所谓的双向开口,意思是可以在头尾两端分别做元素的插入和删除操作,当然,vector也可以在头尾两端插入元素,但是在其头部进行插入操作效率很低。deque 和 vector 最大的差异一是在于 deque 允许使用常数项时间在头部进行元素的插入和删除操作,二是在于 deque 没有容量的概念,因为它是动态的以分段连续空间组合而成,随时可以增加一段新的空间并链接起来。
deque的定义方式:
1 | deque<int> deq; // 定义一个deque,其中的元素为int类型 |
deque的常用内置函数:
1 | //deque中的常用内置函数 |
3.6 map/multimap
map/multimap 又称映射,定义在 <map> 头文件中,map 和 multimap 的底层实现机制都是红黑树。map 的功能是能够将任意类型的元素映射到另一个任意类型的元素上,并且所有的元素都会根据元素的键值自动排序。map 所有的元素都是 pair,同时拥有键值和实值(即 (key, value) 对),key 被视为键值,value 被视为实值,map 不允许两个元素有相同的键值。multimap 和 map 的操作类似,唯一区别是 multimap 的键值允许重复。
map/multimap的定义方式:
1 | map<string, int> mp; // 定义一个将string映射成int的map |
map/multimap的常用内置函数:
1 | // map/multimap中的常用内置函数 |
3.7 set/multiset
set/multiset 又称集合,定义在 <set> 头文件中。set 的特性是所有元素都会根据元素的键值自动被排序,set 的元素不像 map 那样可以同时拥有键值和实值,set 的元素既是键值又是实值,set 不允许两个元素有相同的键值,因此总结来说就是 set 中的元素是有序且不重复的。multiset 的特性和用法和 set 完全相同,唯一的区别在于 multiset 允许有重复元素,set 和 multiset 的底层实现都是红黑树。
set/multiset的定义方式:
1 | set<int> st; // 定义一个set,其中的元素类型为int |
set/multiset的常用内置函数:
1 | // set/multiset中的常用内置函数 |
3.8 unordered_map/unordered_set
unordered_map/unordered_set 分别定义在 <unordered_map> 与 <unordered_set> 头文件中,内部采用的是 hash 表结构,拥有快速检索的功能。与 map/set 相比最大的区别在于 unordered_map/unordered_set 中的元素是无序的,增删改查的时间复杂度为 O(1)(map/set 增删改查的时间复杂度为 O(logn)),但是不支持 lower_bound()/upper_bound() 函数。
unordered_map/unordered_set的定义方式:
1 | unordered_set<int> st; // 定义一个unordered_set,其中的元素类型为int |
unordered_map/unordered_set的常用内置函数:
1 | // unordered_map/unordered_set中的常用内置函数 |
4. STL算法
C++ 标准库定义了一组泛型算法,之所以称为泛型指的是它们可以操作在多种容器上,不但可以作用于标准库类型,还可以用在内置数组类型甚至其它类型的序列上。泛型算法定义在 <algorithm> 头文件中,标准库还定义了一组泛化的算术算法(Generalized Numeric Algorithm),定义在 <numeric> 头文件中。使用方法如下:
1 |
|