C++STL常用容器整理归纳

本篇较为详细的讲解算法竞赛中常用到的STL内的几种常用容器及函数,其中包括各种容器的特点、函数、在算法竞赛中的应用,以及我对各种容器的理解。文中对容器理解有误的地方,请多多指正。
本篇持续更新中…

vector(数组)

vector可理解为变长数组,也就是不定长数组,在声明一个vector型的元素作为数组时,不需要声明它的长度。
vector支持随机访问,对于任意下标 0 <= i <= n,可以像数组一样用[ i ]取值,但它不支持在任意位置O(1)插入。

迭代器

迭代器就相当于STL中的指针,迭代器的声明方式为:

vector<int>::iterator it;	//保存int型的vector容器的迭代器

vector的迭代器的操作方式与指针相似,两个迭代器可以进行相加减。迭代器与整数相加就相当于指针的移动;两个迭代器相减也和指针相减含义一样,其结果就是两个迭代器对应下标的距离。
vector中的beginend函数分别返回的是vector容器中的头部和尾部,相当于v[0]v[v.size()]
按照数组方式和迭代器方式遍历整个数组:

for(int i = 0; i < v.size(); i ++ ) cout << v[i] << " "; 
cout << endl;
for(vector<int>::iterator it = v.begin(); it != v.end(); it ++ ) cout << *it << " "; 
cout << endl;

vector中的常用函数

STL的所有函数都支持size和empty函数,下文介绍中将不再赘述

//声明
#include<iostream>
#include<vector>			//vector的头文件

vector<int> v;				//声明一个int型的vector容器
vector<int> a[114]			//声明一个int型的二维vector容器,相当于二维数组
struct node {int x, y, z;};	//
vector<node> b;				//声明一个node型的vector容器

int main(void) {
	for(int i = 0; i < 10; i ++ ) {
		v.push_back(i);
	}
	std::cout << v.size() << endl;		//返回v的实际长度(包含的元素个数)
	/*返回:10 */
	std::cout << v.empty() << endl;		//判断是否为空,当v为空时返回true/1,否则返回false/0
	/*返回:0  */
	//v.clear();						//清空容器
	std::cout << v.front() << endl;		//返回当前v的首个元素
	/*返回:0  */
	std::cout << v.back() << endl;		//返回当前v的最后一个元素
	/*返回:9  */
	v.pop_back();						//删除v的最后一个元素	
	return 0;
}

vector数组实现sort排序

sort(s.begin(), s.end(), [&](auto a, auto b){return a > b});

【实例】用vector代替邻接表保存有向图

coust int MAX_EDGES = 100010;
vector<int> ver[MAX_EDGES], edge[MAX_EDGES];
//保存从x到y权值为z的有向边
void add(int x, int y, int z) {
	ver[x].push_back(y);
	edge[x].push_back(z);
}
//遍历从x出发的所有边
for(int i = 0; i < ver[x].size(); i ++ ) {
	int y = ver[x][i], z = edge[x][i];
}

queue(队列)

队列顾名思义,它的存储方式就像队列一样,是按照插入队列的顺序进行排序,是一种先进先出的思想。
queue队列经常在图的广度优先搜索bfs最短路算法spfa中应用。

queue中的常用函数

#include<queue>							//queue容器的头文件

queue<int> q;							//
struct node {int x, y, z;};	
queue<node> qq;							//声明一个结构体类型的队列容器

q.push(111);							//将111插入队尾 O(1)
q.pop();								//将队头元素删除 O(1)
int x = q.front();						//返回队头元素 O(1)
int y = q.back();						//返回队尾元素 O(1)

priority_queue(优先队列)

priority_queue 可以理解为一个大根二叉堆。

关于二叉堆的介绍:https://blog.csdn.net/weixin_73523694/article/details/131626953

#include<queue>

priority_queue<int> q;				//声明
priority_queue<pair<int, int>> qq;	//声明一个二元组类型的队列

q.push(111);						//将元素插入队列中 O(log n)
q.pop();							//删除队头元素, O(log n)
int x = q.top();					//返回队列中最大元素,O(1)

通常二叉堆结构支持将元素插入堆、删除堆顶元素、查询堆顶元素、删除堆中任意元素四种操作,但是用priority_queue不支持删除堆中任意元素。
虽然不支持删除任意元素,但是我们可以模拟删除的操作。我们只需将待删除元素标记,当从堆顶取出元素被标记时,我们可以忽略此次操作,重复进行本次操作,即可实现删除操作。这种操作也被称作懒惰删除法

priority_queue默认大根堆,即队头元素为最大值,但当我们我们储存的是自定义的结构体类型时,需要我们重载 " < " 运算符。
例如下面的node结构体储存了二维平面的编号id和坐标(x, y),比较大小时,先比较横坐标,再比较纵坐标。

struct node{int id; double x, y;};
const double eps = 1e-8;
bool operator < (const node &a, const node &b) {
	return a.x + eps < b.x || a.x < b.x + eps && a.y < b.y;
}

priority_queue默认大根堆,那我们想要使用小根堆时该如何处理呢?

  1. 当堆中储存的元素类型为int或double型时,我们可以在将元素存入堆中时,将元素取相反数,然后取出元素时,再将元素取相反数,即实现小根堆。
  2. 我们还可以直接定义完整的优先队列,实现小根堆。
  3. 通用的做法:重载 " < "运算符。
priority_queue <int,vector<int>,greater<int> > q;	//小根堆
priority_queue <int,vector<int>,less<int> >q;		//大根堆
//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。\
其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)
struct node{int id; double v;};
bool operator < (const node &a, const node &b){
	return a.v > b.v;
}

deque(双端队列)

双端队列deque是一个支持在两端高效插入或删除元素的连续线性储存空间。它像是vector和queue的结合。与vector相比,deque在头部增删元素仅需要O(1)的时间;与queue相比,deque像数组一样支持随机访问。

deque中的常用函数

#include<deque>							//deque的头文件
deque<int> q;							//声明一个int型的deque容器
int x = q[i];							//随机访问
int y = q.front(), z = q.back();		//返回队头/尾元素
q.push_back(x);							//从队尾入队
q.push_front(x);						//从队头入队
q.pop_front();							//删除队头元素
q.pop_back();							//删除队尾元素
q.clear();								//清空容器

set(有序集合)

set有序集合,顾名思义,set容器内部支持自动去重排序,set不支持插入重复的元素。

multiset(有序多重集合)

multiset有序多重集合,支持排序,不支持去重,容器内部可包含多个相等元素。
set和multiset的内部实现是一棵红黑树(平衡树的一种),它们支持的函数基本相同。

s.insert(key);			//向set中插入一个元素key。
s.erase(key);			//从set中移除指定的元素key。
s.clear();				//清空set中的所有元素。
s,begin();				//指向集合中最小元素的迭代器
s.end();				//指向集合中最大元素的下一个位置的迭代器
s.find(key);			//查找set中是否存在指定的元素key,如果存在则返回指向该元素的迭代器,否则返回set::end()。
s.count(key);			//返回set中与指定元素key相等的元素的个数,由于set中不允许有重复的元素,因此返回值只能是0或1。
s.lower_bound(key);		//返回指向第一个不小于给定键值key的元素的迭代器。
s.upper_bound(key);		//返回指向第一个大于给定键值key的元素的迭代器。

除了上述常用的内置函数外,还可以使用迭代器遍历set中的所有元素。需要注意的是,set中的元素是按照键值进行排序的,默认情况下是按照升序排列,也可以通过自定义比较函数来指定排序方式。

unordered_set(不排序集合)

unordered_set是一个集合容器,它存储唯一的元素,并且元素是无序的。它的元素可以是任何类型,但是必须是唯一的。与set相比,unordered_set的插入、删除和查找操作都更快,但是它的元素是无序的。

undered_set<int> s;
s.insert(key);			//向unordered_set中插入一个元素key。
s.erase(key);			//从unordered_set中移除指定的元素key。
s.clear()				//清空unordered_set中的所有元素。
s.find(key);			//查找unordered_set中是否存在指定的元素key,如果存在则返回指向该元素的迭代器,否则返回unordered_set::end()。
s.count(key);			//返回unordered_set中与指定元素key相等的元素的个数,由于unordered_set中不允许有重复的元素,因此返回值只能是0或1。

除了上述常用的内置函数外,还可以使用迭代器遍历unordered_set中的所有元素。

stack(栈)

stack是一种先进后出(LIFO)的数据结构,只能从栈顶进行插入和删除操作,不支持在其他位置访问或修改元素。

stack<int> stk;
stk.push(x);//将x加入栈中,即入栈操作 
stk.pop();//出栈操作(删除栈顶),只是出栈,没有返回值
stk.top();//返回第一个元素(栈顶元素)
stk.size();//返回栈中的元素个数
stk.empty();//当栈为空时,返回 true

map(映射)

map容器是一个键值对 key - value 的映射。其内部实现是一棵以key为关键码的红黑树。map的key和value可以是任意类型,其中key必须定义“小于号”运算符

声明方式:map<key_type, value_type name;

例如:

 map<long long, bool> vis;
 map<string, int> hash;
 map<pair<int, int>, vector<int> > test;

在很多时候,map容器被当作Hash表使用,建立从复杂信息key(如字符串)到简单信息value(如一定范围内的整数)的映射。
因为map基于平衡树实现,所以它的大部分操作的时间复杂度都在O(log n)级别,比Hash函数实现的传统Hash表慢。

unordered_map

unordered_map是C++标准库中的一个关联容器,它提供了一种键值对(key-value)的映射,其中的键是唯一的,而值则可以重复。与map容器相比,unordered_map使用哈希表(hash table)实现,因此它具有更快的插入、查找和删除操作的能力,但是迭代顺序是不确定的。
迭代器操作:unordered_map和map都支持迭代器的使用,可以通过迭代器遍历容器中的元素,包括正向遍历和反向遍历。

map / undered_map 内置函数

map<string, int> h;
undered_map<string, int> operator;
h.insert()				//插入键值对到map中。有多种形式的insert()函数重载,可以接受单个键值对、迭代器范围或者初始化列表作为参数。
h.erase()				//从map中删除指定键的键值对。有多种形式的erase()函数重载,可以接受单个键、迭代器范围或者删除满足特定条件的键值对。
h.find()				//查找指定键的位置,并返回指向该位置的迭代器。如果键不存在,则返回map的end()迭代器。
h.count()				//返回指定键在map中的个数。由于map中的键是唯一的,因此返回值只能是0或1。
h.size()				//返回map中键值对的个数。
h.empty()				//检查map是否为空。如果map为空,则返回true,否则返回false。
h.clear()				//清空map,删除所有的键值对。
operator[]				//访问指定键的值。如果键存在,则返回对应的值;如果键不存在,则会插入一个新的键值对,关联的值被默认构造。

如果多次查找的key不存在,当时间一长,容器里会多很多无用的0值,占用内存。

在使用 [ ] 操作符之前,最好先用find函数检查key的存在性

使用unordered_map容器的示例代码

#include <iostream>
#include <unordered_map>
#include <string>

int main() {
    // 创建一个unordered_map容器
    std::unordered_map<int, std::string> myMap;

    // 插入键值对
    myMap.insert({1, "one"});
    myMap.insert({2, "two"});
    myMap[3] = "three";

    // 访问值
    std::cout << myMap[1] << std::endl;  // 输出: one

    // 检查键是否存在
    if (myMap.count(2) > 0) {
        std::cout << "Key 2 exists." << std::endl;
    }

    // 遍历键值对
    for (const auto& pair : myMap) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }
    
    // 删除键值对
    myMap.erase(3);

    return 0;
}

string(字符串)

string字符串,是c++中的一个类,也可看作是一个字符串容器,可以说是非常好用。它可以直接将两个字符串进行比较。另外其内置函数功能也是十分齐全。

#include<cstring>
string str;
str.push_back('s');//在末尾添加字符 ‘s’
str.pop_back();//删除最后一个元素
str.front();//返回第一元素
str.back();//返回最末元素
str.clear();//清空容器
str.empty();//容器为空返回true, 否则返回 false
str.erase(3);//删除 str[3]~str[n-1], 返回str
str.erase(2, 3);//删除 str[2]~str[2+3-1]
str.erase(str.begin());//删除指向的元素, 返回迭代器, 指向下一元素
str.erase(str.begin(), str.end());//删除区间内的元素, 返回迭代器, 指向下一元素
str.find('s');//返回字符 ‘s’ 在 str 中首次出现的位置
str.find('s', 2);//返回字符 ‘s’ 在 str[2]~str[n-1] 中首次出现的位置
str.find(ch);//返回字符串 ch 在 str 中首次出现的位置
str.find(ch, 4);//返回 ch 在 str[4]~str[n-1] 中首次出现的位置
str.find(ch, 4, 3);//返回 ch[0]~ch[3-1] 在 str[4]~str[n-1] 中首次出现的位置
str.find(str1);//返回 str1 在 str 中首次出现的位置
str.find(str1, 2);//返回 str1 在 str[2]~str[n-1] 中首次出现的位置
str.substr(2);//返回 str[2]~str[n-1], 不对 str 进行操作
str.substr(2,3);//返回 str[2]~str[2+3-1]
str.replace('a', 'b');//讲st字符串中第一个‘a’替换为‘b’
str.replace(0, 3, "abc");//将str字符串中0-3位置的字符串替换为"abc"
str.replace(3, 4, 5, 'x');//用5个'x'字符替换str中从3开始的长度为4的字符串

stringstream

std::stringstream是C++标准库中的一个类,它提供了字符串流的功能,可以将数据以字符串的形式进行输入和输出。std::stringstream可以将各种类型的数据转换为字符串,并且可以从字符串中提取数据。

#include<iostream>
#include<algorithm>
#include<sstream>
using namespace std;
int main(){
	//1、创建一个对象,向对象输入字符串,输入字符串后直接进行字符串拼接
    stringstream ss;
    ss << str;
	ss1 << "fre";
    ss3 << "gre";
    cout << ss1.str() << endl;
    //2、创建时使用字符串初始化,进行字符串拼接时,首先把原本的字符串覆盖掉,之后再进行拼接。
    streamstring ss(str);
    //3、输出时需调用str()函数
    cout << ss.str() << endl;
    //4、利用stringstream去除字符串空格,streamstring 默认是以空格来分割字符串
    stringstream ss("2 dfjho 43");
    cout << ss.str() << endl;
    string str;
    while (ss >> str){
        cout << str << endl;
    }
    //5、利用 streamstring 指定字符分割字符串
    string source = "abc,123,<!>";
    stringstream ss(source);
    cout << ss.str() << endl;
	cout<< endl;
    string str;
    while (getline(ss, str, ',')){
        cout << str << endl;
    }
}

std::stringstream还提供了其他一些功能,如clear()函数用于重置字符串流的状态,str()函数用于获取字符串流的内容,以及seekg()和seekp()函数用于设置读写位置。

std::stringstream对于将数据转换为字符串或从字符串中提取数据非常有用,尤其在需要处理字符串形式的数据时。

bitset

bitset可看作一个多位二进制数,每8位占用一个字节,相当于采用了状态压缩的二进制数组,并支持基本的位运算。在估算程序运行的时间时,我们一般以32位整数的运算次数位基准,因此n位bitset执行一次位运算的复杂度可视为 n / 32 n/32 n/32,效率较高。

bitset声明时可初始化,可用unsigned或string型来赋值。当用unsigned long值作为bitset对象的初始值时,该值将转化为二进制的位模式。而bitset对象中的位集作为这种位模式的副本。如果bitset类型长度大于unsigned long值的二进制位数,则其余的高阶位置为0如果bitet类型长度小于unsigned long值的二进制位数,则只使用unsigned值中的低阶位,超过bitet类型长度的高阶位将被丢弃

操作符

~s : 返回对bitset s按位取反的结果
&,|,^ : 返回丢两个位数相同的bitset执行按位与、或、异或运算的结果
“>>”,“<<”: 返回把一个bitset右移、左移若干位的结果
==,!= : 比较两个bitset代表的二进制数是否相等

bitset支持[ ]操作符,bitset[i]表示二进制的第i位,可以赋值或取值。

//头文件
#include<bitset>
//声明/初始化
bitset<n> s;				//表示一个n位的二进制数,每位都是0
bitset<n> s(m)				//表示一个n位的二进制数,为m的二进制表示
string s;
bitset<n> s(str)			//将string行转化为bitset型,前提是str为01字符串
bitset<n> s(s, a, b)		//将字符串s的下标a开始,长度为b的子字符串与bitset进行转换

bitset和string是反向转化的,但仅限与string型小于bitset位数的部分,超出bitset位数的部分将被抛弃,在bitset位数内,string反向转换位bitset型。
bitset与整型数转换时,bitset从第0位开始存储整型数二进制下的最低位,当整型数的二进制位数超过bitset位数时,超出部分将不会被读进。
遇到整型数或者string型转bitset型时,当整型数二进制或string长度小于bitset时,bitset的高阶补0。
举个例子:

整型数15,二进制为0100,0001,正常转换为8位bitset型为1000,0010, string型转换同理
但是如果0100,0001转换为4位bitset型则为: 1000,因为bitset是从15的二进制的第0位开始储存
string型的"01000001"转换位4位bitset型则为: 0010,因为bitset是将string的前四位反向读取,而string的后四位抛弃了

常用内置函数

b.any()						//b中是否存在为1的二进制位
b.none()					//b中不存在置为1的二进制位
b.count()					//b中置为1的二进制位的个数
b.size()					//b中二进制位的个数
b.test(x)					//b中在x处的二进制位是否为1?
b.set()						//把b中所有二进制位都置为1
b.set(x)					//把b中在x处的二进制位置为1
b.reset()					//把b中所有二进制位都置为0
b.reset(x)					//把b中在x处的二进制位置为0
b.flip()					//把b中所有二进制位逐位取反
b.flip(x)					//把b中在x处的二进制位取反
b.to_uiong()				//用b中同样的二进制位返回一个unsigned long值
cout << b << endl;			//直接输出b

pair(二元组)

C++内置二元组,其中包含两个元素,分别是first、second。在比较大小中,以第一元first尾第一关键字,第二元second尾第二关键字。

pair<int> a;								A is an int-type binary
a.first = 1; a.second = 2;
pair<int, pair<int, int>> b;
b.first = 1;
b.second.first = 2; b.second.second = 3;
```>>,<<: 返回
```cpp
//头文件
#include<bitset>
//声明
bitset<n> s;				//表示一个n位的二进制数,每位都是0
bitset<n> s(m)				//表示一个n位的二进制数,为m的二进制表示
string s;
bitset<n> s(str)			//将string行转化为bitset型,前提是str为01字符串,string对象和bitset对象之间是反向转化的
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值