史上STL最最最最最最全工具书,看我就够了

本文详尽介绍了C++ STL中的容器,包括序列式容器(数组、向量、双端队列、双链表、正向链表)和关联式容器(map、multimap、set、multiset)的创建、初始化、成员函数及其应用场景。重点讲解了容器的插入、删除操作,以及迭代器的使用。此外,还涉及了无序关联式容器(unordered_map、unordered_multimap、unordered_set、unordered_multiset)和特殊容器(栈、队列、优先队列)的创建与使用。文章适合有一定数据结构基础的C++学习者参考。
摘要由CSDN通过智能技术生成

写在前面:这里是小王成长日志,一名在校大学生,想在学习之余将自己的学习笔记分享出来,记录自己的成长轨迹,帮助可能需要的人。欢迎关注与留言。

注意:这篇博客默认:

第一,你已经明了基本的数据结构的知识,例如什么是队列什么是栈什么是散列表什么是字典,没有这些基础的数据结构知识看下去是比较艰难的,同时我认为学习STL也许要先具有基本的数据结构知识,因为STL就是对各种常见的数据结构的存储和常用函数的实现。

第二,这篇博客并不是教程,STL的教程可以去看C语言编程网的STL教程,那是一篇很不错的教程,而本文,只是一部我认为不错的STL工具书,凡是STL中常用的容器都一一罗列,包括各种创建与初始化还有其内置函数列表,在使用STL的过程中又不清晰的地方都可以利用这篇博客速查。

最后,这篇博客大部分整理自C语言编程网的教程和网络上的博客,并对其中未涉及的内容也进行了补充,祝食用愉快。

文章目录

1. 容器

容器种类 功能
序列容器 主要包括 vector 向量容器、list 列表容器以及 deque 双端队列容器。
之所以被称为序列容器,是因为元素在容器中的位置同元素的值无关,即容器不是排序的
将元素插入容器时,指定在什么位置,元素就会位于什么位置。
排序容器 包括 set 集合容器、multiset多重集合容器、map映射容器以及 multimap 多重映射容器。
排序容器中的元素默认是由小到大排序好的,即便是插入元素,元素也会插入到适当位置。所以关联容器在查找时具有非常好的性能
哈希容器 C++ 11 新加入 4 种关联式容器,分别是 unordered_set 哈希集合、unordered_multiset 哈希多重集合、unordered_map 哈希映射以及 unordered_multimap 哈希多重映射。
和排序容器不同,哈希容器中的元素是未排序的,元素的位置由哈希函数确定

1.1 序列式容器

1. 数组(array)

array<T,N>(数组容器):表示可以存储 N 个 T 类型的元素,元素个数固定。就是一个添加了一些成员函数和全局函数的数组,效率不降的情况下比普通数组更加安全。

数组-使用前

在使用数组容器之前,代码中需引入<array>头文件,并默认使用 std 命令空间,如下所示:

#include <array>
using namespace std;
数组-创建与初始化

array 容器有多种初始化方式,具体如下:

  • 创建具有 4 个 int 类型元素的 array 容器:
std::array<double, 10> values;//若已指定std命名空间,则可省略std::
  • 创建时将所有的元素初始化为 0 或者和默认元素类型等效的值:
std::array<double, 10> values {
   };
  • 像创建常规数组那样对元素进行初始化
std::array<double, 10> values {
   0.5,1.0,1.5,2.0};//如同常规数组一样,初始化时未涉及的其余元素都会被初始化为0
数组-array容器成员函数汇总

重要的函数已加粗

成员函数 功能
begin() 返回指向容器中第一个元素的随机访问迭代器。
end() 返回指向容器最后一个元素之后一个位置的随机访问迭代器,通常和 begin() 结合使用。
rbegin() 返回指向最后一个元素的随机访问迭代器。
rend() 返回指向第一个元素之前一个位置的随机访问迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上增加了 const 属性,不能用于修改元素。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crbegin() 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crend() 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
size() 返回容器中当前元素的数量,其值始终等于初始化 array 类的第二个模板参数 N。
max_size() 返回容器可容纳元素的最大数量,其值始终等于初始化 array 类的第二个模板参数 N。
empty() 判断容器是否为空,和通过 size()==0 的判断条件功能相同,但其效率可能更快。
at(n) 返回容器中 n 位置处元素的引用,该函数自动检查 n 是否在有效的范围内,如果不是则抛出 out_of_range 异常。
front() 返回容器中第一个元素的直接引用,该函数不适用于空的 array 容器。
back() 返回容器中最后一个元素的直接应用,该函数同样不适用于空的 array 容器。
data() 返回一个指向容器首个元素的指针。利用该指针,可实现复制容器中所有元素等类似功能。
fill(val) 将 val 这个值赋值给容器中的每个元素。
array1.swap(array2) 交换 array1 和 array2 容器中的所有元素,但前提是它们具有相同的长度和类型。

部分函数使用示例

#include <iostream>
//需要引入 array 头文件
#include <array>
using namespace std;
int main()
{
   
    std::array<int, 4> values{
   };
    //初始化 values 容器为 {0,1,2,3}
    for (int i = 0; i < values.size(); i++) {
   
        values.at(i) = i;
    }
    //使用 get() 重载函数输出指定位置元素
    cout << get<3>(values) << endl;
    //如果容器不为空,则输出容器中所有的元素
    if (!values.empty()) {
   
        for (auto val = values.begin(); val < values.end(); val++) {
   
            cout << *val << " ";
        }
    }
}
数组-访问数组元素
  • 调用size()函数返回个数使用for循环遍历每个元素
	array<int, 5> values{
   1,2,3,4,5};
    //从下标 0 一直遍历到 size()-1 处
    for (int i = 0; i < values.size(); i++) 
        cout << values[i] << " ";
    
  • 使用迭代器进行访问-利用begin()/end() 函数
	array<int, 5> values{
   1,2,3,4,5};
    for (auto first = values.begin(); first < values.end(); ++first) 	
        cout << *first << " ";
    

如上示例,我们可使用at()函数,但同时也可使用[]+索引的方式

  • at()函数会检查越界,而索引的方式则不会进行检查
values.at (4) = values.at(3) + 2.O*values.at(1);
values[4] = values[3] + 2.O*values[1];

同理我们可使用get()函数和索引的方式获取元素值

2. 向量(vector)

vector(向量容器):用来存放 T 类型的元素,是一个长度可变的序列容器,长度不足时会自动申请内存空间以增长。在尾部增删效率为O(1),但在其他位置增删效率为O(n)

向量-使用前

vector 容器以类模板 vector( T 表示存储元素的类型)的形式定义在 <vector> 头文件中,并位于 std 命名空间中。因此,在创建该容器之前,代码中需包含如下内容:

#include <vector>
using namespace std;
向量-创建与初始化

创建 vector 容器的方式有很多,大致可分为以下几种。

  • 创建一个空的存储double类型的vector容器
std::vector<double> values;
  • 创建时初始化值
std::vector<int> primes {
   2, 3, 5, 7, 11, 13, 17, 19};
  • 创建时指定元素个数
std::vector<double> values(20);//这20个元素默认初始值都是0
  • 创建时指定元素个数并指定默认初始值
std::vector<double> values(20, 1.0);//这 20 个元素的初始值都是 1.0
  • 通过存储元素类型相同的其它 vector 容器,可以创建新的 vector 容器
std::vector<char>value1(5, 'c');
std::vector<char>value2(value1);
//若不想保存所有元素
int array[]={
   1,2,3};
std::vector<int>values(array, array+2);//values 将保存{1,2}
std::vector<int>value1{
   1,2,3,4,5};
std::vector<int>value2(std::begin(value1),std::begin(value1)+3);//value2保存{1,2,3}
向量-vector 容器的成员函数
成员函数 功能
begin() 返回指向容器中第一个元素的迭代器。
end() 返回指向容器最后一个元素所在位置后一个位置的迭代器,通常和 begin() 结合使用。
rbegin() 返回指向最后一个元素的迭代器。
rend() 返回指向第一个元素所在位置前一个位置的迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crbegin() 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crend() 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
size() 返回实际元素个数。
max_size() 返回元素个数的最大值。这通常是一个很大的值,一般是 232-1,所以我们很少会用到这个函数。
resize() 改变实际元素的个数。
capacity() 返回当前容量。
empty() 判断容器中是否有元素,若无元素,则返回 true;反之,返回 false。
reserve() 增加容器的容量。
shrink _to_fit() 将内存减少到等于当前元素实际所使用的大小。
operator[ ] 重载了 [ ] 运算符,可以向访问数组中元素那样,通过下标即可访问甚至修改
at() 使用经过边界检查的索引访问元素。
front() 返回第一个元素的引用。
back() 返回最后一个元素的引用。
data() 返回指向容器中第一个元素的指针。
assign() 用新元素替换原有内容。
push_back() 在序列的尾部添加一个元素。
pop_back() 移出序列尾部的元素。
insert() 在指定的位置插入一个或多个元素。
erase() 移出一个元素或一段元素。
clear() 移出所有的元素,容器大小变为 0。
swap() 交换两个容器的所有元素。
emplace() 在指定的位置直接生成一个元素。
emplace_back() 在序列尾部生成一个元素。

对于空的 vector 容器来说,begin() 和 end() 成员函数返回的迭代器是相等的,即它们指向的是同一个位置。
所以,对于空的 vector 容器来说,可以通过调用 push_back() 或者借助 resize() 成员函数实现初始化容器的目的。
除此之外,vector 容器在申请更多内存的同时,容器中的所有元素可能会被复制或移动到新的内存地址,这会导致之前创建的迭代器失效。

注意:emplace_back() 和 push_back() 的虽然都是向容器尾部添加一个元素 , 但两者底层实现的机制不同。push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。
因此emplace_back()的效率会更高
insert()和emplace()同理

向量-访问向量元素
  • 调用size()函数返回个数使用for循环遍历每个元素
	vector<int> values{
   1,2,3,4,5};
    //从下标 0 一直遍历到 size()-1 处
    for (int i = 0; i < values.size(); i++) 
        cout << values[i] << " ";
    
  • 使用迭代器进行访问-利用begin()/end() 函数
	vector<int> values{
   1,2,3,4,5};
    for (auto first = values.begin(); first < values.end(); ++first) 	
        cout << *first << " ";
    
  • 如上示例,我们可使用at()函数,但同时也可使用[]+索引的方式
    • at()函数会检查越界,而索引的方式则不会进行检查
values.at (4) = values.at(3) + 2.O*values.at(1);
values[4] = values[3] + 2.O*values[1];
  • 同理我们可使用get()函数和索引的方式获取元素值
  • 我们可以调用front() 和 back()函数,它们分别返回 vector 容器中第一个和最后一个元素的引用,通过利用这 2 个函数返回的引用,可以访问(甚至修改)容器中的首尾元素。
  • 我们也可以调用 data() 成员函数,该函数的功能是返回指向容器中首个元素的指针。通过该指针也可以访问甚至修改容器中的元素
	vector<int> values{
   1,2,3,4,5};
    //输出容器中第 3 个元素的值
    cout << *(values.data() + 2) << endl;
    //修改容器中第 2 个元素的值
    *(values.data() + 1) = 10;
    cout << *(values.data() + 1) << endl;
向量-insert语法格式
语法格式 功能
iterator insert(pos,elem) 在迭代器 pos 指定的位置之前插入一个新元素elem,并返回表示新插入元素位置的迭代器。
iterator insert(pos,n,elem) 在迭代器 pos 指定的位置之前插入 n 个元素 elem,并返回表示第一个新插入元素位置的迭代器。
iterator insert(pos,first,last) 在迭代器 pos 指定的位置之前,插入其他容器(不仅限于vector)中位于 [first,last) 区域的所有元素,并返回表示第一个新插入元素位置的迭代器。
iterator insert(pos,initlist) 在迭代器 pos指定的位置之前,插入初始化列表(用大括号{}括起来的多个元素,中间有逗号隔开)中所有的元素,并返回表示第一个新插入元素位置的迭代器。
  • iterator insert(pos,elem) 在迭代器 pos 指定的位置之前插入一个新元素elem,并返回表示新插入元素位置的迭代器。
 	std::vector<int> demo{
   1,2};
    //第一种格式用法
    demo.insert(demo.begin() + 1, 3);//{1,3,2}
  • iterator insert(pos,n,elem)
    在迭代器 pos 指定的位置之前插入 n 个元素 elem,并返回表示第一个新插入元素位置的迭代器。
	 //第二种格式用法
    demo.insert(demo.end(), 2, 5);//{1,3,2,5,5}
  • iterator insert(pos,first,last)
    在迭代器 pos 指定的位置之前,插入其他容器(不仅限于vector)中位于 [first,last) 区域的所有元素,并返回表示第一个新插入元素位置的迭代器。
	 //第三种格式用法
    std::array<int,3>test{
    7,8,9 };
    demo.insert(demo.end(), test.begin(), test.end());//{1,3,2,5,5,7,8,9}
  • iterator insert(pos,initlist)
    在迭代器 pos 指定的位置之前,插入初始化列表(用大括号{}括起来的多个元素,中间有逗号隔开)中所有的元素,并返回表示第一个新插入元素位置的迭代器。
	//第四种格式用法
    demo.insert(demo.end(), {
    10,11 });//{1,3,2,5,5,7,8,9,10,11}
向量-删除元素

删除 vector 容器元素的几种方式

函数 功能
pop_back() 删除 vector 容器中最后一个元素,该容器的大小(size)会减 1,但容量(capacity)不会发生改变。
erase(pos) 删除 vector 容器中 pos 迭代器指定位置处的元素,并返回指向被删除元素下一个位置元素的迭代器。该容器的大小(size)会减 1,但容量(capacity)不会发生改变。
swap(beg)、pop_back() 先调用 swap() 函数交换要删除的目标元素和容器最后一个元素的位置,然后使用 pop_back() 删除该目标元素。
erase(beg,end) 删除 vector 容器中位于迭代器 [beg,end)指定区域内的所有元素,并返回指向被删除区域下一个位置元素的迭代器。该容器的大小(size)会减小,但容量(capacity)不会发生改变。
remove() 删除容器中所有和指定元素值相等的元素,并返回指向最后一个元素下一个位置的迭代器。值得一提的是,调用该函数不会改变容器的大小和容量。
clear() 删除 vector 容器中所有的元素,使其变成空的 vector 容器。该函数会改变 vector 的大小(变为 0),但不是改变其容量。
3. 双端队列

deque<T>(双端队列容器):和 vector 非常相似,区别在于使用该容器不仅尾部插入和删除元素高效,在头部插入或删除元素也同样高效,时间复杂度都是 O(1) 常数阶,但是在容器中某一位置处插入或删除元素,时间复杂度为 O(n) 线性阶;

注意,其实除此之外,stack 和 queue 本质上也属于序列容器,只不过它们都是在 deque 容器的基础上改头换面而成,通常更习惯称它们为容器适配器,有关它们的介绍,会放到后续章节中。

队列-使用前

deque 容器以模板类 deque(T 为存储元素的类型)的形式在 头文件中,并位于 std 命名空间中。因此,在使用该容器之前,代码中需要包含下面两行代码:

#include <deque>
using namespace std;
队列-创建与初始化

创建 deque 容器,根据不同的实际场景,可选择使用如下几种方式。

  • 创建一个没有任何元素的空 deque 容器:
std::deque<int> d;
  • 创建一个具有 n 个元素的 deque 容器,其中每个元素都采用对应类型的默认值:
std::deque<int> d(10);
  • 创建一个具有 n 个元素的 deque 容器,并为每个元素都指定初始值,例如:
std::deque<int> d(10, 5)
  • 在已有 deque 容器的情况下,可以通过拷贝该容器创建一个新的 deque 容器,例如:
std::deque<int> d1(5);
std::deque<int> d2(d1);
  • 通过拷贝其他类型容器中指定区域内的元素(也可以是普通数组),可以创建一个新容器,例如:
//拷贝普通数组,创建deque容器
int a[] = {
    1,2,3,4,5 };
std::deque<int>d(a, a + 5);
//适用于所有类型的容器
std::array<int, 5>arr{
    11,12,13,14,15 };
std::deque<int>d(arr.begin()+2, arr.end());//拷贝arr容器中的{13,14,15}
队列-deque容器的成员函数

重要函数已加粗标出

函数成员 函数功能
begin() 返回指向容器中第一个元素的迭代器。
end() 返回指向容器最后一个元素所在位置后一个位置的迭代器,通常和 begin() 结合使用。
rbegin() 返回指向最后一个元素的迭代器。
rend() 返回指向第一个元素所在位置前一个位置的迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crbegin() 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crend() 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
size() 返回实际元素个数。
max_size() 返回容器所能容纳元素个数的最大值。这通常是一个很大的值,一般是 232-1,我们很少会用到这个函数。
resize() 改变实际元素的个数。
empty() 判断容器中是否有元素,若无元素,则返回 true;反之,返回 false。
shrink _to_fit() 将内存减少到等于当前元素实际所使用的大小。
at() 使用经过边界检查的索引访问元素。
front() 返回第一个元素的引用。
back() 返回最后一个元素的引用。
assign() 用新元素替换原有内容。
push_back() 在序列的尾部添加一个元素。
push_front() 在序列的头部添加一个元素。
pop_back() 移除容器尾部的元素。
pop_front() 移除容器头部的元素。
insert() 在指定的位置插入一个或多个元素。
erase() 移除一个元素或一段元素。
clear() 移出所有的元素,容器大小变为 0。
swap() 交换两个容器的所有元素。
emplace() 在指定的位置直接生成一个元素。
emplace_front() 在容器头部生成一个元素。和 push_front() 的区别是,该函数直接在容器头部构造元素,省去了复制移动元素的过程。
emplace_back() 在容器尾部生成一个元素。和 push_back() 的区别是,该函数直接在容器尾部构造元素,省去了复制移动元素的过程。

关于empalace()函数和insert()的效率问题前面已经讨论 这里不再涉及

队列-访问队列中的元素
  • 队列可以像普通数组一样采用索引的方式访问元素
	deque<int>d{
    1,2,3,4 };
    cout << d[1] << endl;
    //修改指定下标位置处的元素
    d[1] = 5;
    cout << d[1] << endl;
  • 队列可以调用at()函数来访问元素
    • at() 成员函数会自行判定访问位置是否越界,如果越界则抛出std::out_of_range异常。
	deque<int>d{
    1,2,3,4 };
    cout << d.at(1) << endl;//2
    d.at(1) = 5;
    cout << d.at(1) << endl;//5
    //下面这条语句会抛出 out_of_range 异常
    //cout << d.at(10) << endl;
  • 队列可以调用front()和back()函数来访问队列的首尾元素
	deque<int> d{
    1,2,3,4,5 };
    cout << "deque 首元素为:" << d.front() << endl;//1
    cout << "deque 尾元素为:" << d.back() << endl;//5
    //修改首元素
    d.front() = 10;
    cout << "deque 新的首元素为:" << d.front() << endl;//10
    //修改尾元素
    d.back() = 20;
    cout << "deque 新的尾元素为:" << d.back() << endl;//20
  • 注意,deque没有提供data()函数
队列-insert() 成员函数语法格式
语法格式 功能
iterator insert(pos,elem) 在迭代器 pos 指定的位置之前插入一个新元素elem,并返回表示新插入元素位置的迭代器。
iterator insert(pos,n,elem) 在迭代器 pos 指定的位置之前插入 n 个元素 elem,并返回表示第一个新插入元素位置的迭代器。
iterator insert(pos,first,last) 在迭代器 pos 指定的位置之前,插入其他容器(不仅限于vector)中位于 [first,last) 区域的所有元素,并返回表示第一个新插入元素位置的迭代器。
iterator insert(pos,initlist) 在迭代器 pos 指定的位置之前,插入初始化列表(用大括号{}括起来的多个元素,中间有逗号隔开)中所有的元素,并返回表示第一个新插入元素位置的迭代器。
	std::deque<int> d{
    1,2 };
    //第一种格式用法
    d.insert(d.begin() + 1, 3);//{1,3,2}
    //第二种格式用法
    d.insert(d.end(), 2, 5);//{1,3,2,5,5}
    //第三种格式用法
    std::array<int, 3>test{
    7,8,9 };
    d.insert(d.end(), test.begin(), test.end());//{1,3,2,5,5,7,8,9}
    //第四种格式用法
    d.insert(d.end(), {
    10,11 });//{1,3,2,5,5,7,8,9,10,11}

注意:下图为vector的insert()函数语法格式,注意到两者是一致的!
在这里插入图片描述

4. 双链表

list<T>(链表容器):是一个长度可变的、由 T 类型元素组成的序列,它以双向链表的形式组织元素,在这个序列的任何地方都可以高效地增加或删除元素。

实际场景中,如何需要对序列进行大量添加或删除元素的操作,而直接访问元素的需求却很少,这种情况建议使用 list 容器存储序列。

双链表-使用前

list 容器以模板类 list(T 为存储元素的类型)的形式在头文件中,并位于 std 命名空间中。因此,在使用该容器之前,代码中需要包含下面两行代码:

#include <list>
using namespace std
  • 104
    点赞
  • 125
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值