C++ STL数据结构总结
vector
介绍
特性
动态数组,可随时添加或删除元素,头文件 #include<vector>
初始化
1 | // 一维初始化 |
函数
| 代码 | 功能 |
|---|---|
c.front() |
返回第一个数据 O(1) |
c.pop_back() |
删除最后一个数据 O(1) |
c.push_back(val) |
尾部添加一个数据 O(1) |
c.size() |
返回数组大小 O(1) |
c.clear() |
清除所有元素 O(n) |
c.resize(n, \val) |
改变数组大小为 n 并赋为 val,默认赋值为 0 |
c.insert(it, x) |
向任意迭代器 it 插入一个元素 x,O(n) |
c.erase(s, t) |
删除 [s, t) 的所有元素 O(n) |
c.begin() |
返回首元素迭代器(地址)O(1) |
c.end() |
返回最后一个元素 后一个位置 的迭代器 O(1) |
c.empty() |
判断是否为空,为空返回真,反之返回假 O(1) |
- 例:
c.insert(c.begin()+2,-1)表示将 -1 插入 c[2] 的位置c.resize(n, \val)中\表示可选参数,不必须c.end()返回最后一个元素后面一个地址,遵循【左闭右开】原则
排序:
使用 sort 排序要: sort(c.begin(), c.end());
访问
下标访问
和数组一样使用
1 | for(int i = 0; i < 5; i++) |
迭代器访问
类似指针
1 | //方式一: |
智能指针
1 | vector<int> v; |
stack
介绍
栈为数据结构的一种,是 STL 中实现的一个先进后出,后进先出的容器。
1 | //头文件 |
函数
| 代码 | 含义 |
|---|---|
s.push(val) |
元素 val 入栈,O(1) |
s.pop() |
移除栈顶元素 O(1) |
s.top() |
取得栈顶元素(但不删除)O(1) |
s.empty() |
检测栈内是否为空,空为真 O(1) |
s.size() |
返回栈内元素的个数 O(1) |
栈模拟
栈只能对栈顶元素进行操作,如果想要进行遍历,只能将栈中元素一个个取出来存在数组中
可以用数组模拟栈,用一个存放下标的变量
top模拟指向栈顶的指针
1 | int s[100]; // 从左至右为栈底到栈顶 |
queue
介绍
队列是一种先进先出的数据结构。
1 | //头文件 |
函数
| 代码 | 含义 |
|---|---|
q.front() |
返回队首元素 O(1) |
q.back() |
返回队尾元素 O(1) |
q.push(val) |
尾部添加一个元素 val 进队 O(1) |
q.pop() |
删除第一个元素 出队 O(1) |
q.size() |
返回队列中元素个数 O(1) |
q.empty() |
判断是否为空,空为真 O(1) |
队列模拟
使用 q[] 数组模拟队列,hh 表示队首元素的下标,初始值为 0,tt 表示队尾元素的下标,初始值为 -1,表示刚开始队列为空。
1 | int q[100]; |
deque
介绍
首尾都可插入和删除的队列为双端队列。
1 | //头文件 |
函数
| 代码 | 含义 |
|---|---|
push_back(x)/push_front(x) |
把x插入队尾后 / 队首 O(1) |
back()/front() |
返回队尾 / 队首元素 O(1) |
pop_back() / pop_front() |
删除队尾 / 队首元素 O(1) |
erase(it) |
删除双端队列中的地址为 it 的元素 |
erase(s, t) |
删除双端队列中 [s, t) 中的元素 |
empty() |
判断 deque 是否空 O(1) |
size() |
返回 deque 的元素数量 O(1) |
clear() |
清空 deque |
deque可以进行排序
1 | //从小到大 |
priority_queue
介绍
优先队列是在正常队列的基础上加了优先级,保证每次的队首元素都是优先级最大的。可以实现每次从优先队列中取出的元素都是队列中 优先级最大 的一个。它的底层是通过 堆 实现的。
函数
| 代码 | 含义 |
|---|---|
q.top() |
访问队首元素 O(1) |
q.push() |
入队 O(log(n)) |
q.pop() |
堆顶(队首)元素出队 O(log(n)) |
q.size() |
队列元素个数 O(1) |
q.empty() |
是否为空 O(1) |
优先级
基本数据类型
1 | priority_queue<int> pq; // 默认大根堆, 即每次取出的元素是队列中的最大值 |
参数解释:
vector<int>是用来承载底层数据结构堆的容器less<int>表示数字大的优先级大,堆顶为最大的数字greater<int>表示数字小的优先级大,堆顶为最小的数字
自定义排序
麻烦,不建议
1 | struct cmp1 { |
结构体
1 | //要排序的结构体(存储在优先队列里面的) |
自定义排序
1 | struct cmp { |
方式二:
重载 < 符号
方式一:
1 | struct node { |
方式二:
1 | struct node { |
优先队列自定义排序规则和
sort()函数定义cmp函数很相似,但是最后返回的情况是 相反 的。
特殊类型
默认先对 pair 的 first 进行降序排序,然后再对 second 降序排序。
map
介绍
映射类似于函数的对应关系,每个 x 对应一个 y ,而 map 是每个键对应一个值。
1 | //头文件 |
map会按照键的顺序从小到大自动排序,键的类型必须可以比较大小
函数
| 代码 | 含义 |
|---|---|
mp.find(key) |
返回键为 key 的映射的迭代器,数据不存在时返回 mp.end() O(logN) |
mp.erase(it) |
删除迭代器对应的键和值 O(1) |
mp.erase(key) |
根据映射的键删除键和值 O(logN) |
mp.erase(first,last) |
删除左闭右开区间迭代器对应的键和值 O(last−first) |
mp.size() |
返回映射的对数 O(1) |
mp.clear() |
清空map中的所有元素 O(N) |
mp.insert() |
插入元素,插入时要构造键值对 O(logN) |
mp.empty() |
空为真 O(1) |
mp.begin() |
返回指向map第一个元素的迭代器 O(1) |
mp.end() |
返回指向map尾部的迭代器(最后一个元素的下一个地址)O(1) |
mp.rbegin() |
返回指向map最后一个元素的迭代器 O(1) |
mp.rend() |
返回指向map第一个元素前面一个的迭代器 O(1) |
mp.count(key) |
查看元素个数 O(logN) (0或1) |
mp.lower_bound() |
返回一个迭代器,指向键值 >= key 的第一个元素 O(logN) |
mp.upper_bound() |
返回一个迭代器,指向键值 > key 的第一个元素 O(logN) |
查找元素是否存在时,可以使用
①mp.find()②mp.count()③mp[key]
但是第三种情况,如果不存在对应的key时,会自动创建一个键值对(产生一个额外的键值对空间)所以为了不增加额外的空间负担,最好使用前两种方法
遍历 map
1 | map<int,int> mp; |
添加元素
1 | map<string,string> mp; |
访问元素
1 | // 迭代器访问 |
unordered_map
内部实现原理
map:内部用红黑树实现,具有自动排序(按键从小到大)功能。
unordered_map:内部用哈希表实现,内部元素无序杂乱。
效率比较
map:
- 优点:内部用红黑树实现,内部元素具有有序性,查询删除等操作复杂度为O(logN)
- 缺点:占用空间,红黑树里每个节点需要保存父子节点和红黑性质等信息,空间占用较大。
unordered_map:
- 优点:内部用哈希表实现,查找速度非常快(适用于大量的查询操作)。
- 缺点:建立哈希表比较耗时。
使用
[]查找元素时,如果元素不存在,两种容器都是创建一个空的元素,所以查询容器内部元素的最优方法是:先判断存在与否,再索引对应值
还有一种映射:multimap,键可以重复,即一个键对应多个值
set
介绍
set 容器中的元素不会重复,当插入集合中已有的元素时,并不会插入进去,而且 set 容器里的元素自动从小到大排序。即 set 里面的元素不重复 且有序
1 | //头文件 |
函数
| 代码 | 含义 |
|---|---|
s.begin() |
返回 set 容器的第一个元素的迭代器 O(1) |
s.end() |
返回 set 容器的最后一个元素的下一个迭代器 O(1) |
s.rbegin() |
返回逆序迭代器,指向容器元素最后一个位置 O(1) |
s.rend() |
返回逆序迭代器,指向容器第一个元素前面的位置 O(1) |
s.clear() |
删除set容器中的所有的元素 O(N) |
s.empty() |
空为真 O(1) |
s.insert() |
插入一个元素,返回插入地址的迭代器和是否插入成功的 bool 并成的 pair O(logN) |
s.size() |
返回当前set容器中的元素个数 O(1) |
s.erase(iterator) |
删除定位器iterator指向的值 O(logN) |
s.erase(first,second) |
删除定位器first和second之间的值 O(logN) |
s.erase(key_value) |
删除键值key_value的值 O(logN) |
s.find(element) |
查找set中的某一元素,有则返回该元素对应的迭代器,无则返回结束迭代器 O(logN) |
s.count(element) |
查找set中的元素出现的个数 (0或1)O(logN) |
s.lower_bound(k) |
返回大于等于k的第一个元素的迭代器 O(logN) |
s.upper_bound(k) |
返回大于k的第一个元素的迭代器 O(logN) |
比较
基本数据结构
greater函数
1 | set<int> s1; // 默认从小到大排序 |
重载运算符
1 | struct cmp { |
初始化时使用匿名函数定义比较规则
1 | set<int, function<bool(int, int)>> s([&](int i, int j){ |
结构体
直接重载结构体运算符即可,让结构体可以比较。
1 | struct Point { |
其他set
multiset:元素可以重复,且元素有序unordered_set:元素无序且只能出现一次unordered_multiset: 元素无序可以出现多次
pair
介绍
pair只含有两个元素,可以看作是只有两个元素的结构体。
应用:
- 代替二元结构体
- 作为map键值对进行插入
1 | //头文件 |
访问
1 | //定义结构体数组 |
string
介绍
string 是一个字符串类,和 char 型字符串类似。可以把 string 理解为一个字符数组。可以用 [] 访问单个字符。
1 | // 头文件 |
特性
支持比较运算符。
string 字符串支持常见的比较操作符 (>,>=,<,<=,==,!=),支持 string 与 C-string 的比较(如 str < "hello" )。按 字典序 排大小。
支持 + 运算符。代表拼接字符串。
1 | string s1 = "123"; |
string 是 C++ 的一个类,专门实现字符串的相关操作。具有丰富的操作方法,数据类型为 string ,字符串结尾没有 \0 字符
读入
读入一个字符串,遇空格,回车结束
1 | string s; |
读入一行字符串,遇回车结束
1 | string s; |
注意:
getline(cin, s)会获取前一个输入的换行符,需要在前面添加读取换行符的语句。如:getchar()或cin.get(),或者直接忽略:cin.ignore()getline 函数是取一整行,行末的换行符也会被读取,但不会被保存,所以获得的内容不包含换行符,且可以直接读取下一行
cin 和 cout 解锁
代码(写在main函数开头):
1 | ios::sync_with_stdio(false); |
为什么要进行解锁?
在一些题目中,读入的数据量很大,往往超过了1e5(10^5^)的数据量,而 cin 和 cout 的读入输出的速度很慢(为了兼容C语言的读入输出在性能上做了妥协),远不如 scanf 和 printf 的速度。所以对 cin 和 cout 进行解锁其速度几乎接近 scanf 和 printf ,避免输入输出超时。
cin cout解锁使用时,不能与scanf,getchar, printf,cin.getline()混用,一定要注意,会出错。
string 向 char 数组的转换
1 | string s = "liano"; |
函数
获取长度
| 代码 | 含义 |
|---|---|
s.size()和s.length() |
返回 string 对象的字符个数,他们执行效果相同 |
s.max_size() |
返回 string 对象最多包含的字符数 |
s.capacity() |
重新分配内存之前,string 对象能包含的最大字符数 |
插入
| 代码 | 含义 |
|---|---|
s.push_back(ch) |
在末尾插入字符 ch |
s.insert(pos, ch) |
在 pos 位置插入 ch |
s.append(str) |
在 s 字符串结尾添加 str 字符串 |
删除
| 代码 | 含义 |
|---|---|
erase(p) |
删除字符串中 p 所指的字符 |
erase(s, t) |
删除字符串中迭代器区间 [s,t) 上所有字符 |
erase(pos, len) |
删除字符串中从索引位置 pos 开始的 len 个字符 |
clear() |
删除字符串中所有字符 |
字符替换
| 代码 | 含义 |
|---|---|
s.replace(pos,n,str) |
把当前字符串从索引 pos 开始的 n 个字符替换为 str |
s.replace(pos,n,n1,ch) |
把当前字符串从索引 pos 开始的 n 个字符替换为 n1个字符 ch |
s.replace(it1,it2,str) |
把当前字符串 [it1,it2) 区间替换为 str |
tolower(s[i]) |
转换为小写 |
toupper(s[i]) |
转换为大写 |
大小写转换可以通过 transform 函数实现
1 | string s; |
前2个指定要转换的容器的起止范围,第3个参数是结果存放容器的起始位置,第4个参数是一元运算。
分割
| 代码 | 含义 |
|---|---|
s.substr(pos,n) |
截取从pos索引开始的n个字符 |
查找
| 代码 | 含义 |
|---|---|
s.find(str, pos) |
从 pos 索引位置(默认为0)开始,查找子串 str,返回索引,-1 表示查找不到 |
s.find(c, pos) |
从 pos 索引位置开始,查找字符 c,返回索引 |
s.rfind(str, pos) |
从 pos 索引位置开始,反向查找子串 str,返回索引 |
s.rfind(c,pos) |
从 pos 索引位置开始,反向查找字符 c,返回索引 |
s.find_first_of(str, pos) |
从 pos 索引位置开始,查找子串 str 中的字符,返回第一次出现的位置 |
s.find_first_not_of (str,pos) |
从 pos 索引位置开始,查找不在子串 str 中的字符,返回第一次出现的位置 |
s.find_last_of(str, pos) |
从 pos 索引位置开始,查找最后一个位于子串 str 的字符,返回找到的位置 |
s.find_last_not_of ( str, pos) |
从 pos 索引位置开始,查找最后一个不位于子串 str 的字符,返回找到的位置 |
排序
1 | sort(s.begin(), s.end()); //按ASCII码排序 |
bitset
介绍
类似数组,但每一个元素只能是 0 或 1,每个元素只用 1 bit空间
1 | //头文件 |
初始化
| 代码 | 含义 |
|---|---|
bitset<n> a |
a 有 n 位,每位都为0 |
bitset<n> a(b) |
a 是 unsigned long 型 b 的一个副本 |
bitset<n> a(s) |
a 是二进制字符串 s 中含有的位串的副本,前面用 0 补充 |
bitset<n> a(s,pos,n) |
a 是 s 中从位置 pos 开始的 n 个位的副本 |
特性
位操作
1 | bitset<4> foo(string("1001")); |
访问
可以通过 [] 访问元素(类似数组)
1 | bitset<4> foo ("1011"); |
函数
| 代码 | 含义 |
|---|---|
b.any() |
b 中是否有 1 的,有 返回 true |
b.none() |
b 中是否没有 1 ,没有 返回 true |
b.count() |
b 中为 1 的个数 |
b.size() |
b 中二进制位的个数 |
b.test(pos) |
测试 b 在 pos 位置是否为 1,是 返回 true |
b[pos] |
返回 b 在 pos 处的二进制位 |
b.set() |
把 b 中所有位都置为 1 |
b.set(pos) |
把 b 中 pos 位置置为 1 |
b.reset() |
把 b 中所有位都置为 0 |
b.reset(pos) |
把 b 中 pos 位置置为 0 |
b.flip() |
把 b 中所有二进制位取反 |
b.flip(pos) |
把 b 中 pos 位置取反 |
b.to_ulong() |
用 b 中同样的二进制位返回一个 unsigned long 值 |
array
介绍
array 是 C++11 新增的容器,效率与普通数据相差无几,比 vector 效率要高,自身添加了一些成员函数。
和其它容器不同,array 容器的大小是固定的,无法动态的扩展或收缩,只允许访问或者替换存储的元素。
1 | // 头文件 |
不同于数组的是对元素类型不做要求,可以套结构体
1 | array<string, 2> s = {"ha", string("haha")}; |
访问
1 | array<int, 4> a = {1, 2, 3, 4}; |
函数
| 成员函数 | 功能 |
|---|---|
begin() |
返回容器中第一个元素的迭代器 |
end() |
返回容器最后一个元素之后一个位置的迭代器 |
rbegin() |
返回最后一个元素的迭代器 |
rend() |
返回第一个元素之前一个位置的迭代器 |
size() |
返回容器中元素的数量,其值等于初始化 array 类的第二个模板参数 n |
max_size() |
返回容器可容纳元素的最大数量,其值始终等于初始化 array 类的第二个模板参数 n |
empty() |
判断容器是否为空 |
at(n) |
返回容器中 n 位置处元素的引用 |
front() |
返回容器中第一个元素的直接引用 |
back() |
返回容器中最后一个元素的直接引用 |
data() |
返回一个指向容器首个元素的指针 |
fill(x) |
将 x 这个值赋值给容器中的每个元素 |
array1.swap(array2) |
交换 array1 和 array2 容器中的所有元素,要求它们具有相同的长度和类型 |
1 | // 将 a 数组 [begin,end) 全部值变为 x |
tuple
介绍
tuple 模板是 pair 的泛化,可以封装不同类型任意数量的对象。可以把 tuple 理解为 pair 的扩展,tuple 可以声明二元组,也可以声明三元组。
tuple 可以等价为结构体使用
1 | // 头文件 |
访问
1 | // 获取tuple对象t的第一个元素 |
函数
1 | tuple<int, int, int> t(1, 2, 3); |
