一、概念
1.1定义
STL:标准模板库,是C++标准库的重要组成部分,STL,英文全称 standard template library,中文可译为标准模板库或者泛型库,其包含有大量的模板类和模板函数,STL 是一些容器、算法和其他一些组件的集合,所有容器和算法都是总结了几十年来算法和数据结构的研究成果,注意,这里提到的容器,本质上就是封装有数据结构的模板类,例如 list、vector、set、map 等......其中list 的底层为双向链表,vector 的底层为顺序表(数组),set 的底层为红黑树,deque 的底层为循环队列,hash_set 的底层为哈希表。
二、vector 动态数组
2.1定义
vector
为大小可以在运行时自动调整,无需手动管理内存(动态数组),定义的vector
数组可以随时添加相同类型数值和删除元素,并通过元素索引进行快速访问。
注:
边界检查:访问元素前检查索引是否小于
size()
,避免越界。- 内存重新分配:当
vector
的大小超过其容量时,它需要重新分配内存。这可能导致性能问题,特别是在频繁添加元素时。- 迭代器失效:在
vector
重新分配内存后,所有指向原vector
的迭代器、指针和引用都会失效。- 容量与大小:
vector
的size()
方法返回的是元素的数量,而capacity()
方法返回的是在不重新分配内存的情况下可以存储的元素的最大数量。避免复制:在赋值或作为函数参数时考虑使用移动语义(C++11及以后)。
自定义类型:如果
vector
包含自定义类型,确保这些类型支持拷贝/移动构造函数和赋值操作符。尾部操作:在
vector
尾部操作(如push_back
)通常比在中间或前端更高效
- 头文件
#include<vector>
- 初始化
- 一维初始化
01 非指定长度初始化
vector<int> a; //定义了一个名为a的一维数组,数组存储int类型数据
vector<double> b; //定义了一个名为b的一维数组,数组存储double类型数据
vector<node> c; //定义了一个名为c的一维数组,数组存储结构体类型数据,node是结构体类型
02 指定长度和初始值的初始化
vector<int> v(n); //定义一个长度为n的数组,初始值默认为0,下标范围[0, n - 1]
vector<int> v(n, 1); //v[0]到v[n-1]所有的元素初始值均为1
注:指定数组长度之后(指定长度后的数组就相当于正常的数组)
03 列表初始化(多个元素的初始化)
vector<int> a{1, 2, 3, 4, 5}; //数组a中有五个元素,数组长度就为5
04 拷贝初始化
vector<int> a(n + 1, 0);
vector<int> b(a); //两个数组中的类型必须相同,a和b都是长度为n+1,初始值都为0的数组
- 二维初始化
01 定义第一维固定长度为5,第二维可变化的二维数组
vector<int> v[5]; //定义可变长二维数组
//注:行不可变(只有5行)而列可变,可以在指定行添加元素
//注:一维固定长度为5,第二维长度可以改变
//注:vector<int> v[5]可以这样理解:长度为5的v数组,数组中存储的是vector<int> 数据类型,而 该类型就是数组形式,故v为二维数组。其中每个数组元素均为空,因为没有指定长度,所以第二维可变长。可以进行下述操作:
v[1].push_back(2);
v[2].push_back(3);
02 初始化二维均可变长数组
vector<vector<int>> v; //定义一个行和列均可变的二维数组
//应用:可以在v数组里面装多个数组
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
03 行列长度均固定 n + 1行 m + 1列初始值为 0
vector<vector<int>> a(n + 1, vector<int>(m + 1, 0));
04 c++17或者c++20支持的形式(不常用),与上面相同的初始化
vector a(n + 1, vector(m + 1, 0));
2.2方法函数
指定数组名称c
代码 | 含义 |
c.front() | 返回vector 中第一个元素的引用 |
c.back() | 返回vector 中最后一个元素的引用 |
c.pop_back() | 删除vector 的最后一个元素 |
c.push_back(element) | 在vector 的末尾添加一个元素 |
c.size() | 返回vector 中元素的数量(unsigned类型) |
c.clear() | 移除容器中的所有元素 |
c.resize(n,value) | 改变vector 的大小,如果n 大于当前大小,则新增的元素会被初始化为value |
c.insert(it,x) | 在it位置之前插入一个新元素x,并返回指向新插入元素的迭代器 |
c.insert(c.begin()+2,-1) | 将-1 插入c[2] 的位置 |
c.erase(first,last) | 移除范围[first, last] 内的所有元素,并返回指向last 之后元素的迭代器。 |
c.begin() | 返回首元素的迭代器(通俗来说就是地址) |
c.end() | 返回最后一个元素后一位置的迭代器(地址) |
c.empty() | 判断是否为空,为空返回真,反之返回假 |
-
排序
使用sort
排序要: sort(c.begin(), c.end());
sort()
为STL函数,请参考本文最后面STL函数系列
对所有元素进行排序,如果要对指定区间进行排序,可以对sort()
里面的参数进行加减改动。
vector<int> a(n + 1);
sort(a.begin() + 1, a.end()); // 对[1, n]区间进行从小到大排序
2.3访问
- 下标法:类比普通数组
注意:一维数组的下标是(0,size-1),访问之外的数会出现越界错误
//添加元素
for(int i = 0; i < 5; i++)
c.push_back(i);
//下标访问
for(int i = 0; i < 5; i++)
std::cout << c[i] << " ";
std::cout << "\n";
- 迭代器法:类似指针一样的访问 ,首先需要声明迭代器变量,和声明指针变量一样,可以根据代码进行理解(附有注释)。
//迭代器访问
vector<int>::iterator it;
//相当于声明了一个迭代器类型的变量it
//通俗来说就是声明了一个指针变量
//方式一:
vector<int>::iterator it = c.begin();
for(int i = 0; i < c.size(); i++)
std::cout << *(it + i) << " ";
std::cout << "\n";
//方式二:
vector<int>::iterator it;
for(it = c.begin(); it != c.end();it ++)
cout << *it << " ";
//c.end()指向尾元素地址的下一个地址
- 智能指针访问
只能遍历完数组,如果要指定的内容进行遍历,需要另选方法。auto 能够自动识别并获取类型
vector<int> v;
v.push_back(12);
v.push_back(241);
for(auto val : v)
{
std::cout << val << " "; // 12 241
}
vector 注:
c[i]
和*(c.begin() + i)
等价vector
和string
的STL
容器支持*(it + i)
的元素访问,其它容器可能也可以支持这种方式访问,但用的不多,可自行尝试。
三、stack 栈
3.1定义
- 栈(Stack)是一种遵循后进先出(LIFO, Last In First Out)原则的有序集合。在这个集合中,添加或删除元素都仅发生在集合的某一端,这一端通常被称为栈顶(Top),而另一端则被称为栈底(Bottom)。栈是一种特殊的线性表,但它只允许在栈顶进行添加(push)或删除(pop)元素的操作。
注:
- 栈的实现方式可以是数组(静态分配或动态增长)或链表。
- 数组实现的栈在访问栈顶元素时具有较高的效率(O(1)时间复杂度),但在动态增长时可能需要额外的内存分配和复制操作。
- 链表实现的栈则具有更好的动态扩展性,但在访问栈顶元素时可能需要更长的遍历时间(尽管在大多数情况下仍然是O(1)时间复杂度,因为链表的头部通常被用作栈顶)。
3.2方法函数
代码 | 含义 |
---|---|
c.push(value) | 将一个新value元素添加到栈顶(压栈) |
c,pop() | 移除栈顶的元素,并返回该元素 (出栈) |
c.top() | 返回栈顶元素的值,但不从栈中移除它 (查看栈顶) |
c.empty() | 检查栈是否为空 (检查栈空) |
c.size() | 返回栈中元素的数量 (获取栈大小) |
2.3 栈遍历
- 栈遍历
栈只能对栈顶元素进行操作,如果想要进行遍历,只能将栈中元素一个个取出来存在数组中
-
组模拟栈进行遍历
通过数组对栈进行模拟,一个存放下标的变量
top
模拟指向栈顶的指针。特点: 比
STL
的stack
速度更快,遍历元素方便int s[100]; // 栈 从左至右为栈底到栈顶 int t = -1; // t 代表栈顶指针,初始栈内无元素,t为-1 for(int i = 0; i <= 5; i++) { //入栈 s[++t] = i; } // 出栈 int top_element = s[t--]; //入栈操作示意 // 0 1 2 3 4 5 // t //出栈后示意 // 0 1 2 3 4 // t
四、queue 队列
4.1定义
- 队列是一种特殊的线性表,它只允许在一端(队尾)进行插入操作,在另一端(队头)进行删除操作。队列遵循先进先出(FIFO, First-In-First-Out)的原则,即最早进入队列的元素将最先被移除
//头文件
#include<queue>
//定义初始化
queue<int> q;
4.2方法函数
代码 | 含义 |
---|---|
q.front() | 返回队列头部的元素 |
q.back() | 返回队列尾部的元素 |
q.push(x) | 队列的尾部插入一个元素x |
q.pop() | 移除队列头部的元素 |
q.size() | 返回队列中元素的个数 |
q.empty() | 检查队列是否为空 |
4.3 队列模拟
使用
q[]
数组模拟队列hh
表示队首元素的下标,初始值为0
tt
表示队尾元素的下标,初始值为-1
,表示刚开始队列为空队列模拟下标很容易弄错,每个人都有自己的队头队尾的下标表示方法,我习惯让队首队尾直接当做元素的下标来访问
#include<bits/stdc++.h> using namespace std; define N 5000 int q[N]; int main() { int h = 0 int t = -1; //入队 q[++t] = 1; q[++t] = 2; //出队 while(h<=t) { int t = q[h++]; printf("%d ",t); } return 0; }
五、deque双端队列
5.1定义
deque
(双端队列)是一种具有队列和栈的性质的抽象数据类型。它允许我们在其首尾端添加(append)或删除(pop)元素的队列
5.2方法函数
代码 | 含义 |
---|---|
d.push_back(x) | 在deque的尾部添加一个元素x |
d.push_front(x) | 在deque的头部添加一个元素x |
d.back() | 返回deque尾部的引用 |
d.front() | 返回deque头部的引用 |
d.pop_back() | 删除deque尾部的元素 |
d.pop_front() | 删除deque头部的元素 |
d.empty() | 检查deque是否为空 |
d.size() | 返回deque中元素的数量 |
d.clear() | 清空 |
d.erase(iterator it) | 删除双端队列中某一元素 |
d.erase(iterator first,iterator last) | 删除队列区间[first,last) 中元素 |
注:
deque可以进行排序
//从小到大
sort(d.begin(),d.end())
//从大到小排序
sort(q.begin(), q.end(), greater<int>());//deque里面的类型需要是int型
sort(q.begin(), q.end(), greater());//高版本C++才可以用
六、priority_queue 优先队列
6.1定义
-
优先队列是在正常队列的基础上加了优先级,优先级最高的元素总是队列的第一个元素。
可以实现每次从优先队列中取出的元素都是队列中优先级最大的一个,它的底层是通过堆来实现的。
//头文件
#include<queue>
//初始化定义
priority_queue<int> q;
6.2方法函数
代码 | 含义 |
---|---|
q.top() | 返回优先队列中优先级最高的元素(但不移除它 |
q.push() | 向优先队列中添加一个元素 |
q.pop() | 移除优先队列中优先级最高的元素(即堆顶元素) |
q.size() | 返回优先队列中元素的数量 |
q.empty() | 检查优先队列是否为空 |
注意没有clear()方法 | |
优先队列只能通过top() 访问队首元素(优先级最高元素) |
6.3设置优先级
6.3.1 基本数据类型的优先级
priority_queue<int> pq; // 默认大根堆, 即每次取出的元素是队列中的最大值
priority_queue<int, vector<int>, greater<int> > q; // 小根堆, 每次取出的元素是队列中的最小值
priority_queue<int, vector<int>, greater<int> >
参数注释:vector<int>:
- int:数据类型
- vector<int>:承载底层数据结构堆的容器,若优先队列中存放的是
double
型数据,就要填vector< double >;
less< int >
表示数字大的优先级大,堆顶为最大的数字greater< int >
表示数字小的优先级大,堆顶为最小的数字
6.3.2 设置数据类型的优先级写法
- 基础写法
priority_queue<int> q1; // 默认大根堆, 即每次取出的元素是队列中的最大值
priority_queue<int, vector<int>, less<int> > q2; // 大根堆, 每次取出的元素是队列中的最大值,同第一行
priority_queue<int, vector<int>, greater<int> > q3; // 小根堆, 每次取出的元素是队列中的最小值
- 自定义排序
struct cmp1
{
bool operator()(int x,int y)
{
return x > y; //顺序
}
};
struct cmp2
{
bool operator()(const int x,const int y)
{
return x < y; //逆序
}
};
priority_queue<int, vector<int>, cmp1> q1; // 小根堆
priority_queue<int, vector<int>, cmp2> q2; // 大根堆
6.3.3 结构体优先级设置
- 即优先队列中存储结构体类型,必须要设置优先级,即结构体的比较运算(因为优先队列的堆中要比较大小,才能将对应最大或者最小元素移到堆顶);
- 优先级设置可以定义在结构体内进行小于号重载,也可以定义在结构体外。
//要排序的结构体(存储在优先队列里面的)
struct Point
{
int x;
int y;
};
- 自定义全局比较结构体规则
//定义的比较结构体
//注意:cmp是个结构体
struct cmp //自定义堆的排序规则
{
bool operator()(const Point& a,const Point& b)
{
return a.x < b.x;
}
};
//初始化定义,
priority_queue<Point, vector<Point>, cmp> q; // x大的在堆顶
- 结构体定义
//方式一
struct node
{
int x;
int y;
friend bool operator < (Point a, Point b) //为两个结构体参数,结构体调用一定要friend
{
return a.x < b.x; //按x从小到大排,x大的在堆顶
}
};
//方式二
struct node
{
int x, y;
bool operator < (const Point &a) const //直接传入一个参数,不必要写friend
{
return x < a.x;//按x升序排列,x大的在堆顶
}
};
- 优先队列定义
priority_queue<Point> q;
注:
优先队列自定义排序规则和
sort()
函数定义cmp
函数很相似,但是最后返回的情况是相反的。即相同的符号,最后定义的排列顺序是完全相反的。
所以只需要记住sort
的排序规则和优先队列的排序规则是相反的。
6.4存储特殊类型的优先级
6.4.1 存储pair类型
- 排序规则
默认先对pair
的first
进行降序排序,然后再对second
降序排序
对first
先排序,大的排在前面,如果first
元素相同,再对second
元素排序,保持大的在前面。
#include<bits/stdc++.h>
using namespace std;
int main()
{
priority_queue<pair<int, int> >q;
q.push({7, 8});
q.push({7, 9});
q.push(make_pair(8, 7));
while(!q.empty())
{
std::cout << q.top().first << " " << q.top().second << "\n";
q.pop();
}
return 0;
}
//输出
8 7
7 9
7 8
七、map映射
7.1定义
- 一种数据结构或容器,用于存储一系列的键值对(key-value pairs),并通过键(key)来快速查找对应的值(value)。它允许存储具有唯一键的元素,每个键都映射到其值。这种映射关系使得通过键可以快速地找到对应的值,而不需要遍历整个容器。
//头文件
#include<map>
//初始化定义
map<string,string> mp;
map<string,int> mp;
map<int,node> mp; //node是结构体类型
//map特性:map会按照键的顺序从小到大自动排序,键的类型必须可以比较大小
7.2方法函数
代码 | 含义 |
---|---|
m.find(key) | 返回指向具有指定键的元素的迭代器。如果未找到该键,则返回end() 迭代器 |
m.erase(it) | 删除指定位置的元素、删除具有指定键的元素或删除一个范围内的元素 |
m.size() | 返回map 中元素的数量 |
m.clear() | 删除map 中的所有元素 |
m.insert() | 向map 中插入一个键值对。如果键已经存在,则不会插入新元素,而是更新该键对应的值(取决于map 使用的值类型的赋值运算符) |
m.empty() | 检查map 是否为空 |
m.begin() | 返回指向map第一个元素的迭代器(地址) |
m.end() | 返回指向map尾部的迭代器(最后一个元素之后位置的地址) |
m.rbegin() | 返回指向map最后一个元素的迭代器(地址) |
m.rend() | 返回指向map第一个元素前面(上一个)的逆向迭代器(地址) |
m.count(key) | 查看元素是否存在,因为map中键是唯一的,所以存在返回1,不存在返回0 |
m.lower_bound(key) | 返回一个迭代器,指向键值>= key的第一个元素 |
m.upper_bound(key) | 返回一个迭代器,指向键值> key的第一个元素 |
注:
- 查找元素是否存在时,可以使用 ① mp.find() ② mp.count() ③ mp[key]
- 但是第三种情况,如果不存在对应的key时,会自动创建一个键值对(产生一个额外的键值对空间)
- 所以为了不增加额外的空间负担,最好使用前两种方法
7.2.1迭代器进行正反向遍历
- 正向遍历map
mp.begin()
和mp.end()
用法
map<int,int> mp;
mp[1] = 2;
mp[2] = 3;
mp[3] = 4;
auto it = mp.begin();
while(it != mp.end())
{
std::cout << it->first << " " << it->second << "\n";
it ++;
}
//输出
1 2
2 3
3 4
- 逆向遍历map
mp.rbegin()
和mp.rend()
map<int,int> mp;
mp[1] = 2;
mp[2] = 3;
mp[3] = 4;
auto it = mp.rbegin();
while(it != mp.rend())
{
std::cout << it->first << " " << it->second << "\n";
it ++;
}
//输出
3 4
2 3
1 2
7.2.2 二分查找
二分查找
lower_bound() upper_bound()
map的二分查找以第一个元素(即键为准),对键进行二分查找返回值为map迭代器类型
#include<bits/stdc++.h> using namespace std; int main() { map<int, int> m{{1, 2}, {2, 2}, {1, 2}, {8, 2}, {6, 2}};//有序 map<int, int>::iterator it1 = m.lower_bound(2); std::cout << it1->first << "\n"; //it1->first=2 map<int, int>::iterator it2 = m.upper_bound(2); std::cout << it2->first << "\n"; //it2->first=6 return 0; }
7.3 添加元素
//先声明
map<string,string> mp;
//方式一:
mp["学习"] = "看书";
mp["玩耍"] = "打游戏";
//方式二:
mp.insert(make_pair("vegetable","蔬菜"));
//方式三:
mp.insert(pair<string,string>("fruit","水果"));
//方式四:
mp.insert({"hahaha","wawawa"});
7.4 访问元素
7.4.1 下标访问
大部分情况用于访问单个元素
include<map> using namespace std; int main() { mp[""] = ""; std::cout << mp[""] << "\n"; return 0; }
7.4.2 遍历访问
- 方式一:迭代器访问
include <map> using namespace std; int main() { map<string,string>::iterator it; for(it = mp.begin(); it != mp.end(); it++) { // 键 值 // it是结构体指针访问所以要用 -> 访问 std::cout << it->first << " " << it->second << "\n"; //*it是结构体变量 访问要用 . 访问 //std::cout<<(*it).first<<" "<<(*it).second; } return 0; }
- 方式二:智能指针
for(auto i : mp) std::cout << i.first << " " << i.second << endl;//键,值
- 方式三:对指定单个元素访问
map<char,int>::iterator it = mp.find('a'); std::cout << it -> first << " " << it->second << "\n";
- 方式四:c++17特性
for(auto [x, y] : mp) std::cout << x << " " << y << "\n"; //x,y对应键和值
7.5 unordered_map的比较
7.5.1 内部实现原理
-
map:内部用红黑树实现,具有自动排序(按键从小到大)功能
-
unordered_map:内部用哈希表实现,内部元素无序杂乱
7.5.2 效率比较
map:
- 优点:内部用红黑树实现,内部元素具有有序性,查询删除等操作复杂度为O(logN)
- 缺点:占用空间,红黑树里每个节点需要保存父子节点和红黑性质等信息,空间占用较大。
unordered_map:
- 优点:内部用哈希表实现,查找速度非常快(适用于大量的查询操作)。
- 缺点:建立哈希表比较耗时。
两者方法函数基本一样,差别不大。注意:
- 随着内部元素越来越多,两种容器的插入删除查询操作的时间都会逐渐变大,效率逐渐变低。
- 使用 [ ] 查找元素时,如果元素不存在,两种容器都是创建一个空的元素;如果存在,会正常索引对应的值。所以如果查询过多的不存在的元素值,容器内部会创建大量的空的键值对,后续查询创建删除效率会大大降低。
- 查询容器内部元素的最优方法是:先判断存在与否,再索引对应值(适用于这两种容器)
// 以 map 为例 map<int, int> mp; int x = 999999999; if(mp.count(x)) // 此处判断是否存在x这个键 std::cout << mp[x] << "\n"; // 只有存在才会索引对应的值,避免不存在x时多余空元素的创建
另外:
还有一种映射:
multimap
键可以重复,即一个键对应多个值,如要了解,可以自行搜索。
八、set集合
8.1定义
- 集合容器,是一种基本的数据结构,用于存储不重复的元素。它通过键(key)来读取和修改元素,但每个键只能对应一个元素,即不存在键相同的不同元素。
//头文件
#include<set>
//初始化定义
set<int> s;
注:
set
容器内部实际上是有序的,因为它底层通常使用红黑树(或其他平衡二叉搜索树)来实现,但对外表现为无序,即不提供直接通过索引访问元素的方式set
容器中的每个元素都是唯一的,不允许有重复的元素- 向
set
容器中添加元素时,容器会自动根据元素的键值进行排序
8.2方法函数
代码 | 含义 |
---|---|
s.begin() | 返回指向容器中第一个元素的迭代器 |
s.end() | 返回指向容器末尾的迭代器(尾后迭代器) |
s.clear() | 移除容器中的所有元素 |
s.insert() | 插入一个元素 |
s.empty() | 判断容器中是否有元素,若无元素,则返回true ;反之,返回false |
s.find() | 查找set中的某一元素,有则返回该元素对应的迭代器,无则返回结束迭代器 |
s.count(key) | 查找set中的元素出现的个数,由于set中元素唯一,此函数相当于查询key是否出现 |
s.lower_bound(key) | 返回大于等于key的第一个元素的迭代器 |
s.upper_bound(key) | 返回大于key的第一个元素的迭代器 |
8.3 访问
- 迭代器访问
for(set<int>::iterator it = s.begin(); it != s.end(); it++) { std::cout << *it << " "; }
- 智能指针
for(auto i : s) { std::cout << i << endl; }
- 访问最后一个元素
//第一种 std::cout << *s.rbegin() << endl; //第二种 set<int>::iterator iter = s.end(); iter--; std::cout << (*iter) << endl; //打印2; //第三种 std::cout << *(--s.end()) << endl;
8.4 重载<运算符>
- 基础数据类型
方式一:改变set排序规则,set中默认使用less比较器,即从小到大排序。(常用) set<int> s1; // 默认从小到大排序 set<int, greater<int> > s2; // 从大到小排序 方式二:重载运算符。(很麻烦,不太常用,没必要) //重载 < 运算符 struct cmp { bool operator () (const int& u, const int& v) const { // return + 返回条件 return u > v; } }; set<int, cmp> s; for(int i = 1; i <= 10; i++) s.insert(i); for(auto i : s) { std::cout << i << " "; } // 10 9 8 7 6 5 4 3 2 1 方式三:初始化时使用匿名函数定义比较规则 set<int, function<bool(int, int)>> s([&](int i, int j){ return i > j; // 从大到小 }); for(int i = 1; i <= 10; i++) { s.insert(i); } for(auto x : s) { std::cout << x << " "; }
- 高级数据类型(结构体)
struct Point { int x, y; bool operator < (const Point &p) const { // 按照点的横坐标从小到大排序,如果横坐标相同,纵坐标从小到大 if(x == p.x) return y < p.y; return x < p.x; } }; set<Point> s; for(int i = 1; i <= 5; i++) { int x; int y; std::cin >> x >> y; s.insert({x, y}); } /* 输入 5 4 5 2 3 7 3 5 4 8 */ for(auto i : s) { std::cout << i.x << " " << i.y << "\n"; } /* 输出 3 5 3 7 4 8 5 2 5 4 */
8.5 其他
multiset
:元素可以重复,且元素有序unordered_set
:元素无序且只能出现一次unordered_multiset
: 元素无序可以出现多次
九、Pair二元组
9.1 定义
- pair只含有两个元素,可以看作是只有两个元素的结构体
9.2 初始化 & 赋值
//头文件 #include<utility> //1.初始化定义 pair<string, int> p("mqLibrary",1);//带初始值的 pair<string, int> p;//不带初始值的 //2.赋值 p = {"yan", 18}; p = make_pair("yan", 18); p = pair<string, int>("yan", 18);
9.3 应用
- 代替二元结构体
- 作为map键值对进行插入
map<string, int> mp; mp.insert(pair<string, int>("mqLibrary",1)); // mp.insert(make_pair("mqLibrary", 1)); // mp.insert({"mqLibrary", 1});
9.4 访问
//定义结构体数组 pair<int,int> p[20]; for(int i = 0; i < 20; i++) { //和结构体类似,first代表第一个元素,second代表第二个元素 std::cout << p[i].first << " " << p[i].second; }
十、bitset位集
10.1 定义
bitset
是 C++ 标准模板库(STL)中的一个类模板,它提供了一种方便的方式来存储和操作固定大小的位序列(即二进制位)。bitset
通常用于需要处理大量布尔值或位操作的场景,如位掩码、权限设置、状态标记- 在
bitset
头文件中,它类似数组,并且每一个元素只能是0或1,每个元素只用1bit空间 - 头文件
//头文件
#include<bitset>
10.2 初始化定义
//创建bitset
std::bitset<8> bits;
//创建初始化bitset
std::bitset<8> bits("10110010"); // 使用字符串初始化
std::bitset<8> bits2(0b10110010); // 使用 C++14 引入的二进制字面量
10.3 位操作
bitset
支持各种位操作,如&
(按位与)、|
(按位或)、^
(按位异或)、~
(按位取反)、<<
(左移)、>>
(右移)等
bitset<4> foo (string("1001"));
bitset<4> bar (string("0011"));
std::cout << (foo^=bar) << endl;// 1010 (foo对bar按位异或后赋值给foo)
std::cout << (foo&=bar) << endl;// 0001 (按位与后赋值给foo)
std::cout << (foo|=bar) << endl;// 1011 (按位或后赋值给foo)
std::cout << (foo<<=2) << endl;// 0100 (左移2位,低位补0,有自身赋值)
std::cout << (foo>>=1) << endl;// 0100 (右移1位,高位补0,有自身赋值)
std::cout << (~bar) << endl;// 1100 (按位取反)
std::cout << (bar<<1) << endl;// 0110 (左移,不赋值)
std::cout << (bar>>1) << endl;// 0001 (右移,不赋值)
std::cout << (foo==bar) << endl;// false (1001==0011为false)
std::cout << (foo!=bar) << endl;// true (1001!=0011为true)
std::cout << (foo&bar) << endl;// 0001 (按位与,不赋值)
std::cout << (foo|bar) << endl;// 1011 (按位或,不赋值)
std::cout << (foo^bar) << endl;// 1010 (按位异或,不赋值)
10.4 访问
//可以通过 [ ] 访问元素(类似数组),注意最低位下标为0,如下:
bitset<4> foo ("1011");
std::cout << foo[0] << endl; //1
std::cout << foo[1] << endl; //0
std::cout << foo[2] << endl; //1
10.5 方法函数
代码 | 含义 |
---|---|
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数组
11.1 定义
-
是C++11新增的容器,效率与普通数据相差无几,比
vector
效率要高,自身添加了一些成员函数。和其它容器不同,array 容器的大小是固定的,无法动态的扩展或收缩,只允许访问或者替换存储的元素。
注:
array
的使用要在std
命名空间里
11.2使用
- 基础数据类型
1.声明一个大小为100的int型数组,元素的值不确定 std::array<int, 100> a; 2.声明一个大小为100的int型数组,初始值均为0(初始值与默认元素类型等效) std::array<int, 100> a{}; 3.声明一个大小为100的int型数组,初始化部分值,其余全部为0 std::array<int, 100> a{1, 2, 3}; 4.或者可以用等号 std::array<int, 100> a = {1, 2, 3};
- 高级数据结构
不同于数组的是对元素类型不做要求,可以套结构体 std::array<string, 2> s = {"ha", string("haha")}; std::array<node, 2> a;
11.3 取存元素值
11.3.1修改
-
修改元素
array<int, 4> a = {1, 2, 3, 4}; a[0] = 4;
11.3.2 访问
- 访问元素
1.下标访问 array<int, 4> a = {1, 2, 3, 4}; for(int i = 0; i < 4; i++) { std::cout << a[i] << " \n"[i == 3]; } 2.智能指针 for(auto i : a) { std::cout << i << " "; } 3.迭代器访问 auto it = a.begin(); for(; it != a.end(); it++) { std::cout << *it << " "; } 4.at()函数访问 下标为1的元素加上下标为2的元素,答案为5 array<int, 4> a = {1, 2, 3, 4}; int res = a.at(1) + a.at(2); std::cout << res << "\n"; 5.get方法访问 将a数组下标为1位置处的值改为x ⭐️注意⭐️ 获取的下标只能写数字,不能填变量 get<1>(a) = x;
11.4方法函数
成员函数 | 功能 |
---|---|
a.begin() | 返回容器中第一个元素的访问迭代器(地址) |
a.end() | 返回容器最后一个元素之后一个位置的访问迭代器(地址) |
a.rbegin() | 返回最后一个元素的访问迭代器(地址) |
a.rend() | 返回第一个元素之前一个位置的访问迭代器(地址) |
a.size() | 返回容器中元素的数量,其值等于初始化 array 类的第二个模板参数N |
a.max_size() | 返回容器可容纳元素的最大数量,其值始终等于初始化 array 类的第二个模板参数 N |
a.empty() | 判断容器是否为空 |
a.at(n) | 返回容器中 n 位置处元素的引用,函数会自动检查 n 是否在有效的范围内,如果不是则抛出 out_of_range 异常 |
a.front() | 返回容器中第一个元素的直接引用,函数不适用于空的 array 容器 |
a.back() | 返回容器中最后一个元素的直接引用,函数不适用于空的 array 容器。 |
a.data() | 返回一个指向容器首个元素的指针。利用该指针,可实现复制容器中所有元素等类似功能 |
a.fill(x) | 将 x 这个值赋值给容器中的每个元素,相当于初始化 |
a1.swap(a2) | 交换 a1和 a2容器中的所有元素,但前提是它们具有相同的长度和类型 |
11.4.1 使用示例
data()
指向底层元素存储的指针。对于非空容器,返回的指针与首元素地址比较相等
at()
下标为
1
的元素加上下标为2
的元素,答案为5
array<int, 4> a = {1, 2, 3, 4}; int res = a.at(1) + a.at(2); std::cout << res << "\n";
- fill()
1.array的fill()函数,将a数组全部元素值变为x a.fill(x); 2.另外还有其它的fill()函数:将a数组[begin,end)全部值变为x fill(a.begin(), a.end(), x);
- 排序
sort(a.begin(), a.end());
十二、tuple元组
12.1 定义
#include<tuple>
12.2 用法
12.2.1声明与初始化
1.声明一个空的tuple三元组 tuple<int, int, string> t1; 2.赋值 t1 = make_tuple(1, 1, "hahaha"); 3.创建的同时初始化 tuple<int, int, int, int> t2(1, 2, 3, 4); 4.可以使用pair对象构造tuple对象,但tuple对象必须是两个元素 auto p = make_pair("yan", 1); tuple<string, int> t3 {p}; //将pair对象赋给tuple对象
12.2.2 元素操作
- 获取元素
获取tuple对象t的第一个元素 int first = get<0>(t);
- 修改元素
修改tuple对象t的第一个元素 get<0>(t) = 1;
12.3 函数
- 获取元素个数
tuple<int, int, int> t(1, 2, 3); std::cout << tuple_size<decltype(t)>::value << "\n"; // 3
- 获取对应元素的值
1.通过get<n>(obj)方法获取,n必须为数字不能是变量 tuple<int, int, int> t(1, 2, 3); std::cout << get<0>(t) << '\n'; // 1 std::cout << get<1>(t) << '\n'; // 2 std::cout << get<2>(t) << '\n'; // 3
- 通过
tie
解包 获取元素值tie可以让tuple变量中的三个值依次赋到tie中的三个变量中 int one; int three; string two; tuple<int, string, int> t(1, "hahaha", 3); tie(one, two, three) = t; std::cout << one << two << three << "\n"; // 1hahaha3