文章转载自行码棋,并稍作修改
务必做到读万卷书,行万里路。
另外C版本一定要对(可能要加编译参数
-std=c++11
),C11即可,C++17或20更好。
使DEV支持C++20 : https://blog.csdn.net/qq_50285142/article/details/122930647 '\n' vs 'std::endl': link
#vector
#介绍
vector
为可变长数组(动态数组),定义的vector
数组可以随时添加数值和删除元素。
注意:在局部区域中(比如局部函数里面)开vector,是在堆空间里面开的。 在局部区域开array是在栈空间开的,而栈空间比较小,如果开了非常长的array就会发生爆栈。 故局部区域不可以开大长度array,但是可以开大长度
vector
。
- 头文件
|
|
初始化
一维初始化
1 2 3
vector<int> a; //定义了一个名为a的一维数组,数组存储int类型数据 vector<double> b;//定义了一个名为b的一维数组,数组存储double类型数据 vector<node> c;//定义了一个名为c的一维数组,数组存储结构体类型数据,node是结构体类型
指定长度和初始值的初始化
1 2 3
vector<int> v(n);//定义一个长度为n的数组,初始值默认为0,下标范围[0, n - 1] vector<int> v(n, 1);//v[0]到v[n-1]所有的元素初始值均为1 //注意:指定数组长度之后(指定长度后的数组就相当于正常的数组了)
初始化中有多个元素
1
vector<int> a{1, 2, 3, 4, 5};//数组a中有五个元素,数组长度就为5
拷贝初始化
1 2
vector<int> a(n + 1, 0); vector<int> b(a);//两个数组中的类型必须相同,a和b都是长度为n+1,初始值都为0的数组
二维初始化 定义第一维固定长度为
5
,第二维可变化的二维数组1 2 3
vector<int> v[5];//定义可变长二维数组 //注意:行不可变(只有5行), 而列可变,可以在指定行添加元素 //第一维固定长度为5,第二维长度可以改变
vector<int> v[5]
可以这样理解:长度为5的v数组,数组中存储的是vector<int>
数据类型,而该类型就是数组形式,故v
为二维数组。其中每个数组元素均为空,因为没有指定长度,所以第二维可变长。可以进行下述操作:1 2
v[1].push_back(2); v[2].push_back(3);
行列均可变
1 2
//初始化二维均可变长数组 vector<vector<int>> v;//定义一个行和列均可变的二维数组
应用:可以在
v
数组里面装多个数组1 2 3 4 5
vector<int> t1{1, 2, 3, 4}; vector<int> t2{2, 3, 4, 5}; v.push_back(t1); v.push_back(t2); v.push_back({3, 4, 5, 6}) // {3, 4, 5, 6}可以作为vector的初始化,相当于一个无名vector
行列长度均固定
n + 1
行m + 1
列初始值为01
vector<vector<int>> a(n + 1, vector<int>(m + 1, 0));
c++17或者c++20支持的形式(不常用),与上面相同的初始化
1
vector a(n + 1, vector(m + 1, 0));
#方法函数
知道了如何定义初始化可变数组,下面就需要知道如何添加,删除,修改数据。
c指定为数组名称,含义中会注明算法复杂度。
代码 | 含义 |
---|---|
c.front() | 返回第一个数据$O(1)$ |
c.back() | 返回数组中的最后一个数据 $O(1)$ |
c.pop_back() | 删除最后一个数据$O(1)$ |
c.push_back(element) | 在尾部加一个或多个数据push_back((1,2,3)) $O(1)$ |
c.size() | 返回实际数据个数(unsigned类型)$O(1)$ |
c.clear() | 清除元素个数$O(N)$,N为元素个数 |
c.resize(n, v) | 改变数组大小为n ,值为v ,如果没有默认赋值为0 |
c.insert(it, x) | 向任意迭代器it 插入一个元素x ,$O(N)$ |
例:c.insert(c.begin()+2,-1) | 将-1 插入c[2] 的位置 |
c.erase(first,last) | 删除[first,last) 的所有元素,$O(N)$ |
c.begin() /c.rbegin() | 返回首/逆元素的迭代器(通俗来说就是地址)$O(1)$ |
c.end() /c.rend() | 返回最后/逆一个元素后一个位置的迭代器(地址)$O(1)$ |
c.empty() | 判断是否为空,为空返回真,反之返回假 $O(1)$ |
注意: end()
返回的是最后一个元素的后一个位置的地址,不是最后一个元素的地址,所有STL容器均是如此
排序
使用sort
排序要: sort(c.begin(), c.end());
sort()
为STL函数,请参考本文最后面STL函数系列。
对所有元素进行排序,如果要对指定区间进行排序,可以对sort()
里面的参数进行加减改动。
|
|
#访问
- 下标法: 和普通数组一样
注意:一维数组的下标是从$0$到$v.size()-1$,访问之外的数会出现越界错误
- 迭代器法: 类似指针一样的访问 ,首先需要声明迭代器变量,和声明指针变量一样,可以根据代码进行理解(附有注释)。
代码如下:
|
|
#下标访问
直接和普通数组一样进行访问即可。
|
|
#迭代器访问
类似指针。
|
|
#智能指针
只能遍历完数组,如果要指定的内容进行遍历,需要另选方法。 auto 能够自动识别并获取类型。
|
|
vector
注意:
vi[i]
和*(vi.begin() + i)
等价
vector
和string
的STL
容器支持*(it + i)
的元素访问,其它容器可能也可以支持这种方式访问,但用的不多,可自行尝试。
#stack
#介绍
栈为数据结构的一种,是STL中实现的一个先进后出,后进先出的容器。
|
|
#方法函数
代码 | 含义 |
---|---|
s.push(ele) | 元素ele 入栈,增加元素 $O(1)$ |
s.pop() | 移除栈顶元素 $O(1)$ |
s.top() | 取得栈顶元素(但不删除)$O(1)$ |
s.empty() | 检测栈内是否为空,空为真 $O(1)$ |
s.size() | 返回栈内元素的个数 $O(1)$ |
stack/queue没有clear() |
#栈遍历
#栈遍历
栈只能对栈顶元素进行操作,如果想要进行遍历,只能将栈中元素一个个取出来存在数组中
#数组模拟栈进行遍历
通过一个数组对栈进行模拟,一个存放下标的变量top
模拟指向栈顶的指针。
特点: 比STL
的stack
速度更快,遍历元素方便
|
|
#queue
#介绍
队列是一种先进先出的数据结构。
|
|
#方法函数
代码 | 含义 |
---|---|
q.front() | 返回队首元素 $O(1)$ |
q.back() | 返回队尾元素 $O(1)$ |
q.push(element) | 尾部添加一个元素element 进队$O(1)$ |
q.pop() | 删除第一个元素 出队 $O(1)$ |
q.size() | 返回队列中元素个数,返回值类型unsigned int $O(1)$ |
q.empty() | 判断是否为空,队列为空,返回true $O(1)$ |
#队列模拟
使用q[]
数组模拟队列
hh
表示队首元素的下标,初始值为0
tt
表示队尾元素的下标,初始值为-1
,表示刚开始队列为空
|
|
#priority_queue
#介绍
优先队列是在正常队列的基础上加了优先级,保证每次的队首元素都是优先级最大的。
可以实现每次从优先队列中取出的元素都是队列中优先级最大的一个。
它的底层是通过堆来实现的。
|
|
#函数方法
代码 | 含义 |
---|---|
q.top() | 访问队首元素 |
q.push() | 入队 |
q.pop() | 堆顶(队首)元素出队 |
q.size() | 队列元素个数 |
q.empty() | 是否为空 |
注意没有clear() ! | 不提供该方法 |
优先队列只能通过top() 访问队首元素(优先级最高的元素) |
#设置优先级
#基本数据类型的优先级
|
|
参数解释:
第二个参数:
vector< int >
是用来承载底层数据结构堆的容器,若优先队列中存放的是double
型数据,就要填vector< double >
总之存的是什么类型的数据,就相应的填写对应类型。同时也要改动第三个参数里面的对应类型。第三个参数:
less< int >
表示数字大的优先级大,堆顶为最大的数字greater< int >
表示数字小的优先级大,堆顶为最小的数字 int代表的是数据类型,也要填优先队列中存储的数据类型
下面介绍基础数据类型优先级设置的写法。
1. 基础写法(非常常用)
|
|
2. 自定义排序(不常见,主要是写着麻烦)
下面的代码比较长,基础类型优先级写着太麻烦,用第一种即可。
|
|
#结构体优先级设置
即优先队列中存储结构体类型,必须要设置优先级,即结构体的比较运算(因为优先队列的堆中要比较大小,才能将对应最大或者最小元素移到堆顶)。
优先级设置可以定义在结构体内进行小于号重载,也可以定义在结构体外。
|
|
- 版本一:自定义全局比较规则
|
|
- 版本二:直接在结构体里面写
因为是在结构体内部自定义的规则,一旦需要比较结构体,自动调用结构体内部重载运算符规则。
结构体内部有两种方式
方式一
|
|
方式二
|
|
优先队列的定义
|
|
注意: 优先队列自定义排序规则和sort()
函数定义cmp
函数很相似,但是最后返回的情况是相反的。即相同的符号,最后定义的排列顺序是完全相反的。
所以只需要记住sort
的排序规则和优先队列的排序规则是相反的就可以了。
#存储特殊类型的优先级
#存储pair类型
- 排序规则:
默认先对
pair
的first
进行降序排序,然后再对second
降序排序 对first
先排序,大的排在前面,如果first
元素相同,再对second
元素排序,保持大的在前面。
pair
请参考下文
|
|
结果: 8 7 7 9 7 8
#stack、queue、priority_queue小结
它们的构造器都为Container,因此不包含诸如clear()、insert()、erase()函数
#deque
#介绍
首尾都可插入和删除的队列为双端队列。
|
|
#方法函数
代码 | 含义 |
---|---|
push_back(x)/push_front(x) | 把x 插入队尾后 / 队首 $O(1)$ |
back()/front() | 返回队尾 / 队首元素 $O(1)$ |
pop_back() / pop_front() | 删除队尾 / 队首元素 $O(1)$ |
erase(iterator it) | 删除双端队列中的某一个元素 |
erase(iterator first,iterator last) | 删除双端队列中[first,last) 中的元素 |
empty() | 判断deque是否空 $O(1)$ |
size() | 返回deque的元素数量 $O(1)$ |
clear() | 清空deque |
#注意点
deque可以进行排序
|
|
#map
#介绍
映射类似于函数的对应关系,每个x
对应一个y
,而map
是每个键对应一个值。会python的朋友学习后就会知道这和python的字典非常类似。
|
|
map特性:map会按照键的顺序从小到大自动排序,键的类型必须可以比较大小
#函数方法
#函数方法
代码 | 含义 |
---|---|
mp.find(key) | 返回键为key的映射的迭代器 $O(logN)$ 注意:find函数返回一个迭代器。当数据存在时,返回数据所在位置的迭代器,数据不存在时,返回$mp.end()$ |
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() | 如果map为空,返回true,否则返回false |
mp.begin() | 返回指向map第一个元素的迭代器(地址) |
mp.end() | 返回指向map尾部的迭代器(最后一个元素的下一个地址) |
mp.rbegin() | 返回指向map最后一个元素的迭代器(地址) |
mp.rend() | 返回指向map第一个元素前面(上一个)的逆向迭代器(地址) |
mp.count(key) | 查看元素是否存在,因为map中键是唯一的,所以存在返回1,不存在返回0 |
mp.lower_bound() | 返回一个迭代器,指向键值**>= key**的第一个元素 |
mp.upper_bound() | 返回一个迭代器,指向键值**>** key的第一个元素 |
#注意点
下面说明部分函数方法的注意点
注意: 查找元素是否存在时,可以使用 ①
mp.find()
②mp.count()
③mp[key]
但是第三种情况,如果不存在对应的key
时,会自动创建一个键值对(产生一个额外的键值对空间) 所以为了不增加额外的空间负担,最好使用前两种方法
#迭代器进行正反向遍历
mp.begin()
和mp.end()
用法:
用于正向遍历map
|
|
结果:
1 2
2 3
3 4
mp.rbegin()
和mp.rend()
用于逆向遍历map
|
|
结果:
3 4
2 3
1 2
#二分查找
二分查找lower_bound() upper_bound()
map的二分查找以第一个元素(即键为准),对键进行二分查找 返回值为map迭代器类型
|
|
#添加元素
|
|
- 方式一:
|
|
- 方式二:插入元素构造键值对
|
|
- 方式三:
|
|
- 方式四:
|
|
#访问元素
#下标访问
(大部分情况用于访问单个元素)
|
|
#遍历访问
- 方式一:迭代器访问
|
|
- 方式二:智能指针访问
|
|
- 方式三:对指定单个元素访问
|
|
- 方式四:c++17特性才具有
|
|
#与unordered_map的比较
这里就不单开一个大目录讲unordered_map了,直接在map里面讲了。
#内部实现原理
map:内部用红黑树实现,具有自动排序(按键从小到大)功能。
unordered_map:内部用哈希表实现,内部元素无序杂乱。
#效率比较
map:
优点:内部用红黑树实现,内部元素具有有序性,查询删除等操作复杂度为$O(logN)$
缺点:占用空间,红黑树里每个节点需要保存父子节点和红黑性质等信息,空间占用较大。
unordered_map:
- 优点:内部用哈希表实现,查找速度非常快(适用于大量的查询操作)。
- 缺点:建立哈希表比较耗时。
两者方法函数基本一样,差别不大。
注意:
随着内部元素越来越多,两种容器的插入删除查询操作的时间都会逐渐变大,效率逐渐变低。
使用
[]
查找元素时,如果元素不存在,两种容器都是创建一个空的元素;如果存在,会正常索引对应的值。所以如果查询过多的不存在的元素值,容器内部会创建大量的空的键值对,后续查询创建删除效率会大大降低。查询容器内部元素的最优方法是:先判断存在与否,再索引对应值(适用于这两种容器)
1 2 3 4 5
// 以 map 为例 map<int, int> mp; int x = 999999999; if(mp.count(x)) // 此处判断是否存在x这个键 cout << mp[x] << "\n"; // 只有存在才会索引对应的值,避免不存在x时多余空元素的创建
另外:
还有一种映射:
multimap
键可以重复,即一个键对应多个值,如要了解,可以自行搜索。
#set
#介绍
set容器中的元素不会重复,当插入集合中已有的元素时,并不会插入进去,而且set容器里的元素自动从小到大排序。
即:set里面的元素不重复 且有序
|
|
#函数方法
代码 | 含义 |
---|---|
s.begin() | 返回set容器的第一个元素的地址(迭代器)$O(1)$ |
s.end() | 返回set容器的最后一个元素的下一个地址(迭代器)$O(1)$ |
s.rbegin() | 返回逆序迭代器,指向容器元素最后一个位置$O(1)$ |
s.rend() | 返回逆序迭代器,指向容器第一个元素前面的位置$O(1)$ |
s.clear() | 删除set容器中的所有的元素,返回unsigned int类型$O(N)$ |
s.empty() | 判断set容器是否为空$O(1)$ |
s.insert() | 插入一个元素 |
s.size() | 返回当前set容器中的元素个数$O(1)$ |
erase(iterator) | 删除迭代器iterator指向的值 |
erase(first,second) | 删除迭代器first和second之间的值 |
erase(key_value) | 删除键值key_value的值 |
查找 | |
s.find(element) | 查找set中的某一元素,有则返回该元素对应的迭代器,无则返回结束迭代器 |
s.count(element) | 查找set中的元素出现的个数,由于set中元素唯一,此函数相当于查询element是否出现 |
s.lower_bound(k) | 返回大于等于k的第一个元素的迭代器$O(logN)$ |
s.upper_bound(k) | 返回大于k的第一个元素的迭代器$O(logN)$ |
#访问
- 迭代器访问
|
|
- 智能指针
|
|
- 访问最后一个元素
|
|
|
|
|
|
#重载<运算符
- 基础数据类型
方式一:改变set排序规则,set中默认使用less比较器,即从小到大排序。(常用)
|
|
方式二:重载运算符。(很麻烦,不太常用,没必要)
|
|
方式三:初始化时使用匿名函数定义比较规则
|
|
- 高级数据类型(结构体)
直接重载结构体运算符即可,让结构体可以比较。
|
|
#其它set
multiset
:元素可以重复,且元素有序
unordered_set
:元素无序且只能出现一次
unordered_multiset
: 元素无序可以出现多次
#总结
容器 | 迭代器类型 | Container | 有序? | 时间复杂度 | 底层 | 备注 |
---|---|---|---|---|---|---|
array | 随机访问(支持iter+2) | sequence | 随机读改) $O(1)$ | 静态数组 | array<int, 5> ay; 不能扩容 | |
vector | 随机访问 | sequence | 随机读改、尾插/删 $O(1)$ 插/删$O(n)$ | 动态数组 | 是push_back() | |
deque | 随机访问 | sequence | 随机读改、头插/删、尾插/删 $O(1)$ 插/删$O(n)$ | 双端队列(数组) | 是push_back/front() | |
list | 双向访问 | sequence | 插/删$O(1)$ | 双向链表(deque区别) | 是push_back/front() | |
map | 双向访问(支持it--) | associative | 有 | 增/删/查$O(log_n)$(但是多态导致不同复杂度) | 红黑树 | 只有insert() |
unordered_map | 双向访问 | unordered associative | 增/删/查$O(1)$,最差$O(n)$ | 哈希表 | 只有insert() | |
set | 双向访问 | associative | 有 | 增删改$O(log_n)$ | 红黑树 | 只有insert() |
unordered_set | 双向访问 | unordered associative | 增删查$O(1)$,最差$O(n)$ | 哈希表 | 只有insert() | |
stack | 不支持 | Container adaptor | 顶删/增 $O(1)$(FIFO) | deque/list/vector | 不支持列表初始化,只有push() | |
queue | 不支持 | Container adaptor | 尾删/头插 $O(1)$(FILO) | deque/list | 不支持列表初始化,只有push() | |
priority_queue | 不支持 | Container adaptor | 最大/小在顶端 | 插入、删除 $O(log2n)$ | 堆 | 不支持列表初始化,只有push() |
#pair
#介绍
pair只含有两个元素,可以看作是只有两个元素的结构体。
应用:
- 代替二元结构体
- 作为map键值对进行插入(代码如下)
|
|
|
|
#访问
|
|
#string
#介绍
string是一个字符串类,和char
型字符串类似。
可以把string理解为一个字符串类型,像int一样可以定义
#初始化及定义
|
|
简单使用
- 访问单个字符:
|
|
- string数组使用:
|
|
#string 特性
支持比较运算符 string字符串支持常见的比较操作符
(>,>=,<,<=,==,!=)
,支持string
与C-string
的比较(如str < "hello"
)。 在使用>,>=,<,<=
这些操作符的时候是根据“当前字符特性”将字符按字典顺序
进行逐一比较。字典排序靠前的字符小, 比较的顺序是从前向后比较,遇到不相等的字符就按这个位置上的两个字符的比较结果确定两个字符串的大小(前面减后面)。同时,
string ("aa") < string("aaa")
。支持
+
运算符,代表拼接字符串 string字符串可以拼接,通过"+"运算符进行拼接。1 2 3 4
string s1 = "123"; string s2 = "456"; string s = s1 + s2; cout << s; //123456
#读入详解
读入字符串,遇空格,回车结束
|
|
读入一行字符串(包括空格),遇回车结束
|
|
注意: getline(cin, s)
会获取前一个输入的换行符,需要在前面添加读取换行符的语句。如:getchar()
或 cin.get()
错误读取:
|
|
正确读取:
|
|
cin
与cin.getline()
混用cin输入完后,回车,cin遇到回车结束输入,但回车还在输入流中,cin并不会清除,导致
getline()
读取回车,结束。 需要在cin后面加cin.ignore()
;主动删除输入流中的换行符。(不常用)
cin和cout解锁
代码(写在main函数开头):
|
|
为什么要进行
cin
和cout
的解锁,原因是:在一些题目中,读入的数据量很大,往往超过了1e5(10^5)的数据量,而
cin
和cout
的读入输出的速度很慢(是因为cin
和cout
为了兼容C语言的读入输出在性能上做了妥协),远不如scanf
和printf
的速度,具体原因可以搜索相关的博客进行了解。所以对
cin
和cout
进行解锁使cin
和cout
的速度几乎接近scanf
和printf
,避免输入输出超时。
注意:cin cout
解锁使用时,不能与 scanf,getchar, printf,cin.getline()
混用,一定要注意,会出错。
string与C语言字符串(C-string)的区别
- string 是C++的一个类,专门实现字符串的相关操作。具有丰富的操作方法,数据类型为
string
,字符串结尾没有\0
字符- C-string C语言中的字符串,用char数组实现,类型为
const char *
,字符串结尾以\0
结尾
一般来说string向char数组转换会出现一些问题,所以为了能够实现转换,string有一个方法c_str()
实现string向char数组的转换。
|
|
#函数方法
- 获取字符串长度
代码 | 含义 |
---|---|
s.size() 和s.length() | 返回string对象的字符个数,他们执行效果相同。 |
s.max_size() | 返回string对象最多包含的字符数,超出会抛出length_error异常 |
s.capacity() | 重新分配内存之前,string对象能包含的最大字符数 |
- 插入
代码 | 含义 |
---|---|
s.push_back() | 在末尾插入 |
例:s.push_back('a') | 末尾插入一个字符a |
s.insert(iter_pos,element) | 在pos位置插入element,pos为迭代器 |
例:s.insert(s.begin(),'1') | 在第一个位置插入1字符。以上方法只能插入一个字符 |
s.insert(size_t(3), "c"); | |
s.append(str) | 在s字符串结尾添加str字符串 |
例:s.append("abc") | 在s字符串末尾添加字符串“abc” |
- 删除
代码 | 含义 |
---|---|
erase(iterator p) | 删除字符串中p所指的字符 |
erase(iterator first, iterator last) | 删除字符串中迭代器区间[first,last) 上所有字符 |
erase(int_pos, len) | 删除字符串中从索引位置pos开始的len个字符,只有字符串可以用整形索引来删除 |
clear() | 删除字符串中所有字符 |
- 字符替换
代码 | 含义 |
---|---|
s.replace(pos,n,str) | 把当前字符串从索引pos开始的n个字符替换为str |
s.replace(pos,n,n1,c) | 把当前字符串从索引pos开始的n个字符替换为n1个字符c |
s.replace(it1,it2,str) | 把当前字符串[it1,it2) 区间替换为str it1 ,it2为迭代器哦 |
- 大小写转换
法一:
代码 | 含义 |
---|---|
tolower(s[i]) | 转换为小写 |
toupper(s[i]) | 转换为大写 |
法二:
通过stl的transform
算法配合tolower
和toupper
实现。
有4个参数,前2个指定要转换的容器的起止范围,第3个参数是结果存放容器的起始位置,第4个参数是一元运算。
|
|
- 分割
代码 | 含义 |
---|---|
s.substr(pos,n) | 截取从pos索引开始的n个字符,返回该字符串 |
- 查找
代码 | 含义 |
---|---|
s.find (str, pos) | 在当前字符串的pos索引位置(默认为0)开始,查找子串str,返回找到的位置索引,-1表示查找不到子串 |
s.find (c, pos) | 在当前字符串的pos索引位置(默认为0)开始,查找字符c,返回找到的位置索引,-1表示查找不到字符 |
s.rfind (str, pos) | 在当前字符串的pos索引位置开始,反向查找子串s,返回找到的位置索引,-1表示查找不到子串 |
s.rfind (c,pos) | 在当前字符串的pos索引位置开始,反向查找字符c,返回找到的位置索引,-1表示查找不到字符 |
s.find_first_of (str, pos) | 在当前字符串的pos索引位置(默认为0)开始,查找子串s的字符,返回找到的位置索引,-1表示查找不到字符 |
s.find_first_not_of (str,pos) | 在当前字符串的pos索引位置(默认为0)开始,查找第一个不位于子串s的字符,返回找到的位置索引,-1表示查找不到字符 |
s.find_last_of(str, pos) | 在当前字符串的pos索引位置开始,查找最后一个位于子串s的字符,返回找到的位置索引,-1表示查找不到字符 |
s.find_last_not_of ( str, pos) | 在当前字符串的pos索引位置开始,查找最后一个不位于子串s的字符,返回找到的位置索引,-1表示查找不到子串 |
|
|
- 排序
|
|
#bitset
#介绍
bitset 在 bitset 头文件中,它类似数组,并且每一个元素只能是0或1,每个元素只用1bit空间
|
|
#初始化定义
初始化方法
代码 | 含义 |
---|---|
bitset < n >a | a有n位,每位都为0 |
bitset < n >a(b) | a是unsigned long型u的一个二进制副本 |
bitset < n >a(s) | a是string对象s中含有的位串的副本 |
bitset < n >a(s,pos,n) | a是s中从位置pos开始的n个位的副本 |
注意:
n
必须为常量表达式
演示代码:
|
|
#10.3 特性
bitset
可以进行位操作
|
|
访问
|
|
#10.4 方法函数
代码 | 含义 |
---|---|
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 容器的大小是固定的,无法动态的扩展或收缩,只允许访问或者替换存储的元素。
注意:
array
的使用要在std
命名空间里
#声明与初始化
基础数据类型
声明一个大小为100的int
型数组,元素的值不确定
|
|
声明一个大小为100的int
型数组,初始值均为0
(初始值与默认元素类型等效)
|
|
声明一个大小为100的int
型数组,初始化部分值,其余全部为0
|
|
或者可以用等号
|
|
高级数据类型
不同于数组的是,对元素类型不做要求,可以套结构体
|
|
#存取元素
- 修改元素
|
|
- 访问元素
下标访问
|
|
利用auto
访问
|
|
迭代器访问
|
|
at()
函数访问
下标为1
的元素加上下标为2
的元素,答案为5
|
|
get
方法访问
将a
数组下标为1
位置处的值改为x
注意:获取的下标只能写数字,不能填变量
|
|
#成员函数
成员函数 | 功能 |
---|---|
begin() | 返回容器中第一个元素的访问迭代器(地址) |
end() | 返回容器最后一个元素之后一个位置的访问迭代器(地址) |
rbegin() | 返回最后一个元素的访问迭代器(地址) |
rend() | 返回第一个元素之前一个位置的访问迭代器(地址) |
size() | 返回容器中元素的数量,其值等于初始化 array 类的第二个模板参数N |
max_size() | 返回容器可容纳元素的最大数量,其值始终等于初始化 array 类的第二个模板参数 N |
empty() | 判断容器是否为空 |
at(n) | 返回容器中 n 位置处元素的引用,函数会自动检查 n 是否在有效的范围内,如果不是则抛出 out_of_range 异常 |
front() | 返回容器中第一个元素的直接引用,函数不适用于空的 array 容器 |
back() | 返回容器中最后一个元素的直接引用,函数不适用于空的 array 容器。 |
data() | 返回一个指向容器首个元素的指针。利用该指针,可实现复制容器中所有元素等类似功能 |
fill(x) | 将 x 这个值赋值给容器中的每个元素,相当于初始化 |
array1.swap(array2) | 交换 array1 和 array2 容器中的所有元素,但前提是它们具有相同的长度和类型 |
#部分用法示例
data()
指向底层元素存储的指针。对于非空容器,返回的指针与首元素地址比较相等。
|
|
fill()
array的fill()
函数,将a
数组全部元素值变为x
|
|
另外还有其它的fill()
函数:将a
数组$[begin,end)$全部值变为x
|
|
排序
|
|
#tuple
#介绍
tuple模板是pair的泛化,可以封装不同类型任意数量的对象。
可以把tuple理解为pair的扩展,tuple可以声明二元组,也可以声明三元组。
tuple可以等价为结构体使用
头文件
|
|
#声明初始化
声明一个空的tuple
三元组
|
|
赋值
|
|
创建的同时初始化
|
|
可以使用pair对象构造tuple对象,但tuple对象必须是两个元素
|
|
#元素操作
通过get<n>(obj)
方法获取,n
必须为数字不能是变量
|
|
修改tuple对象t
的第一个元素
|
|
#函数操作
- 获取元素个数
|
|
- 通过
tie
解包 获取元素值
tie
可以让tuple变量中的三个值依次赋到tie中的三个变量中
|
|
#STL函数
#accumulate
accumulate(beg, end, init)
复杂度: $O(N)$
作用:对一个序列的元素求和
init
为对序列元素求和的初始值
返回值类型:与init
- 基础累加求和:
|
|
- 自定义二元对象求和:
使用lambda表达式
|
|
#atoi
atoi(const char *)
将字符串转换为
int
类型
注意参数为char
型数组,如果需要将string类型转换为int类型,可以使用stoi
函数(参考下文),或者将string
类型转换为const char *
类型。
关于输出数字的范围:
atoi
不做范围检查,如果超出上界,输出上界,超出下界,输出下界。
stoi
会做范围检查,默认必须在int
范围内,如果超出范围,会出现RE(Runtime Error)错误。
|
|
或者
|
|
#fill
fill(beg,end,num)
复杂度: $O(N)$
对一个序列进行初始化赋值
|
|
注意区分memset:
memset()
是按字节进行赋值,对于初始化赋0
或-1
有比较好的效果.
如果赋某个特定的数会出错,赋值特定的数建议使用fill()
#is_sorted
is_sorted(beg,end)
复杂度: $O(N)$
判断序列是否有序(升序),返回
bool
值
|
|
#iota
iota(beg, end)
让序列递增赋值
|
|
#lower_bound + upper_bound
复杂度: $O(logN)$
作用:二分查找
|
|
#max_element+min_element
复杂度: $O(N)$
找最大最小值
|
|
#max+min
复杂度: $O(1)$
找多个元素的最大值和最小值
|
|
|
|
#minmax
minmax(a, b)
复杂度: $O(1)$
返回一个
pair
类型,第一个元素是min(a, b)
, 第二个元素是max(a, b)
|
|
#minmax_element
minmax_element(beg, end)
复杂度: $O(N)$
返回序列中的最小和最大值组成pair的对应的地址,返回类型为
pair<vector<int>::iterator, vector<int>::iterator>
|
|
#nth_element
nth_element(beg, nth, end)
复杂度: 平均$O(N)$
寻找第序列第n小的值
nth
为一个迭代器,指向序列中的一个元素。下标第n小的值恰好在nth
位置上。
执行nth_element()
之后,序列中的元素会围绕nth进行划分:nth之前的元素都小于等于它,而之后的元素都大于等于它
实例:求序列中的第3小的元素
|
|
#next_permutation
next_permutation(beg, end)
复杂度: $O(N)$
求序列的下一个排列,下一个排列是字典序大一号的排列
返回true
或false
next_permutation(beg,end)
如果是最后一个排列,返回
false
,否则求出下一个序列后,返回true
|
|
应用:求所有的排列
输出a
的所有排列
|
|
prev_permutation(beg,end)
求出前一个排列,如果序列为最小的排列,将其重排为最大的排列,返回false
#partial_sort
partial_sort(beg, mid, end)
复杂度: 大概$O(N logM)$ M
为距离
部分排序,排序mid-beg个元素,mid为要排序区间元素的尾后的一个位置
从beg到mid前的元素都排好序
对a数组前5个元素排序按从小到大排序
|
|
也可以添加自定义排序规则:
partial_sort(beg,mid,end,cmp)
对a的前五个元素都是降序排列
|
|
#random_shuffle
复杂度: $O(N)$
- 随机打乱序列的顺序
- 在
C++14
中被弃用,在C++17
中被废除,C++11之后应尽量使用shuffle
来代替。
|
|
#reverse
reverse(beg,end)
复杂度: $O(N)$
对序列进行翻转
|
|
#sort
复杂度: $O(N logN)$
作用:对一个序列进行排序
|
|
|
|
#stable_sort
复杂度: $O(N logN)$
功能和sort()基本一样
区别在于
stable_sort()
能够保证相等元素的相对位置,排序时不会改变相等元素的相对位置
使用用法和sort()
一样,见上
#stoi
stoi(const string*)
将对应string类型字符串转换为数字
注意参数为string
字符串类型。
关于输出数字的范围:
stoi
会做范围检查,默认必须在int
范围内,如果超出范围,会出现RE(Runtime Error)错误。
atoi
不做范围检查,如果超出上界,输出上界,超出下界,输出下界。
|
|
#transform
复杂度: $O(N)$
作用:使用给定操作,将结果写到dest中
|
|
|
|
#to_string
将数字转化为字符串,支持小数(double)
|
|
#unique
unique(beg, end)
复杂度: $O(N)$
消除重复元素,返回消除完重复元素的下一个位置的地址
如:
a[] = {1, 3, 2, 3, 6}
;unique之后a数组为
{1, 2, 3, 6, 3}
前面为无重复元素的数组,后面则是重复元素移到后面,返回a[4]
位置的地址(不重复元素的尾后地址)
消除重复元素一般需要原序列是有序序列
运用:离散化
|
|
#__gcd
__gcd(a,b)
求a和b的最大公约数
__gcd(12,15) = 3
__gcd(21,0) = 21
#__lg
__lg(a)
- 求一个数二进制下最高位位于第几位(从第0位开始)(或二进制数下有几位)
__lg(x)
相当于返回$\lfloor log_2 x \rfloor$- 复杂度$O(1)$
__lg(8) = 3
__lg(15) = 3
#_builtin 内置位运算函数
内置函数有相应的unsigned lnt
和unsigned long long
版本,unsigned long long
只需要在函数名后面加上ll
就可以了,比如__builtin_clzll(x)
,默认是32位unsigned int
#__builtin_ffs
__builtin_ffs(x)
二进制中对应最后一位
1
的位数,比如4
会返回3
(100)
#__builtin_popcount
__builtin_popcount(x)
x
中1
的个数
#__builtin_ctz
__builtin_ctz(x)
x
末尾0
的个数(count tail zero
)
#__builtin_clz
__builtin_clz(x)
x
前导0
的个数(count leading zero
)
|
|
#__builtin_parity
__builtin_parity(x)
x
中1的个数的奇偶性, 奇数输出1
,偶数输出0
可参考链接:
可能有些人需要PDF文件,公众号【行码棋】回复 STL 获取,抱歉😭
2023.03.28 已更新PDF文件(除去了水印,内容进行了部分排版调整和更新)