文章目录
介绍
1、STL标准模板库
(1)包含三部分(广义上)
- 容器:存储和管理数据对象的集合,是数据结构的泛型化封装。
- 迭代器:不暴露容器内部细节的前提上,位用户提供一组统一的访问容器的方法。
- 泛型算法:借助迭代器,以泛型的方式操作容器中的数据元素。
(2)特性
- 所有的STL组件全部通过模板来实现,支持数据类型的泛化
- STL强调的是将数据结构和算法与具体的数据类型分离开来
- STL的设计思想是在最小的框架内实现最大的弹性
2、STL容器
1.线性容器
vector(向量/动态数组) list(列表) deque(双端队列)
C++11 --- array(静态数组) foward_list(单链表)
2.适配器容器
stack(栈) queue(队列) priority_queue(优先队列)
3.关联容器
map(映射) multimap(多重映射) set(集合) multiset(多重集合)
C++11 --- 四大无序关联容器(unordered_xxx)
容器的共性
- 所有的STL容器都支持深拷贝的拷贝构造和赋值
- 但是元素对象本身要支持深拷贝
- 同类型的容器都重载"==“和”!="运算,容器相等的条件是
- 元素类型相同,元素个数相同,对应元素满足"=="运算
- 存入容器中的是对象的副本,而不是对象本身
线性容器
vector(动态数组)
概念
vector
是STL中最常用的容器之一,它是一个动态数组
,可以根据需要自动调整大小。vector底层使用数组实现,同时也有一些机制来保证其动态性能。
特点
- 当vector中元素数量超过容量时,vector会按照一个预定的增长策略重新分配内存,以便存储更多元素。这个增长策略通常是当前容量的两倍。例如,当容量为10时,当需要添加第11个元素时,vector会将容量增加到20,并将前10个元素复制到新的数组中。
- vector的底层实现还包括一些优化措施,例如移动语义和内存池。移动语义可以避免在向vector添加元素时进行不必要的拷贝操作,从而提高性能。内存池则是通过预先分配一定数量的内存来减少重新分配内存的次数,从而提高内存使用效率。
(1)基本特性
- 使用连续的空间存储数据元素,支持下标运算
- 使用动态内存管理数据,支持自动扩容,但是不自动收缩
- 通过预分配内存空间降低动态内存分配的消耗
- 支持随机的插入和删除,但是在尾部操作的效率最高
(2)使用方法
1.空向量
vector<元素类型> 向量对象; 比如: vector<int> vi;
2.指定初始大小
vector<元素类型> 向量对象(初始大小); 比如: vector<int> vi(10);
3.指定初始大小和初始值
vector<元素类型> 向量对象(初始大小,初始值); 比如: vector<int> vi(10,5);
4.使用其他容器来初始化
int a[5] = {1,2,3,4,5};
vector<int> vi(a , a+5); // 5个 vi(a , a+4); // 4个
(3)迭代器
正向迭代器: iterator
反向迭代器: reverse_iterator
常正向迭代器: const_iterator
常反向迭代器: const_reverse_iterator
(4)成员函数
iterator begin(); // 返回起始正向迭代器
iterator end(); // 返回末尾正向迭代器(最后一个的下一个)
reverse_iterator rbegin(); // 返回起始反向迭代器
reverse_iterator rend(); // 返回末尾反向迭代器(第一个的前一个)
size_t size(); // 大小
size_t capacity(); // 容量
// 调整大小,可增可减,第二个参数时增加时的初始值,增构造,减析构
void resize(size_t size,初始值);
// 调整容量,只增大,不缩小
void reserve(size_t size);
value_type& at(int n); // 通过下标访问元素
value_type& front(); // 首元素
value_type& back(); // 尾元素
iterator insert(iterator pos,const value_type &value); // 从指定位置插入
void push_back(const value_type &value); // 从尾部插入
iterator erase(iterator pos); // 删除指定位置的元素
void pop_back(); // 尾部删除
bool empty(); // 判空
// 对容器的修改可能导致之前的迭代器失效
// 修改容器后要重置之前的迭代器
// 需要顺序存储并且可能频繁在尾部操作就可以选择使用vector
array(静态数组)
概念
STL中的array
是一个定长的数组容器
,它的底层实现与普通的C++数组很相似。
// array在STL中的定义如下:
template<class _Ty, size_t _Size>
class array;
- 其中,T表示数组元素类型,N表示数组长度。
- 在STL中,array是一个类模板,它被实现为一个包含了N个元素的静态数组。array的成员函数和操作符与普通数组很相似,只是添加了一些STL特性,例如迭代器、算法等。
特点
- 可以使用STL的迭代器和算法,使得代码更加简洁、易读。
- 支持一些STL的函数,例如std::sort()和std::binary_search()等,方便进行数据处理和查找。
- 在STL容器中,array是一个定长的数组容器,因此可以在不需要动态增删元素的情况下使用,提高了代码的效率和稳定性。
(1)基本特性
- 使用连续空间存储元素,支持下标运算。
- 容量空间固定,不支持扩容和收缩。
- 支持随机位置访问,末尾操作效率较高。
(2)使用方法
array<元素类型, 数组长度> array对象;
比如: array<int, 5> ai;
// 静态数组支持c语言数组初始化的语法
array<int, 5> ai = {1, 2, 3, 4, 5];
(3)迭代器(同上)
(4)成员函数 …
deque(双端队列)
概念
deque
是 C++ STL 库中提供的一种容器,它允许在头部和尾部进行元素的插入和删除操作。其底层实现主要依赖于一个或多个连续的动态分配的数组,每个数组被称为一个缓冲区(buffer),缓冲区的大小通常是 512 bytes 或 1024 bytes。
特点
- 随机访问效率高:虽然deque支持在队列两端进行插入和删除操作,但它也支持类似数组一样的随机访问。
- 内存使用效率高:由于deque的内部实现是使用多个固定大小的缓冲区来存储元素,而不是像vector那样在一个连续的内存区域中存储元素,因此在一些场景下,deque的内存使用效率会更高。
- 支持高效的插入和删除操作:与vector相比,deque支持在队列两端进行快速的插入和删除操作。对于需要频繁进行这些操作的场景,使用deque可以提高程序的性能。
(1)基本特性
- deque的物理结构几乎和vector一样,唯一的区别是deque的两端都是开放的,都可以插入和删除。
- 接口比vector多了push_front/pop_front,去掉了capacity和reserve函数。
- 性能上deque略低于vector,内存消耗高于vector,元素访问的时间长于vector。
- deque空间对称,在头部和尾部操作的时间复杂度相同。
(2)使用方法
deque<元素类型> deque对象;
list(双向链表)
概念
在C++ STL中,list
是一个双向链表容器,其中每个元素都包含指向前一个元素和后一个元素的指针。list底层实现的核心是链表数据结构,因此它没有提供随机访问功能。在list中,访问元素通常需要沿着链表进行迭代,从而导致访问速度比数组和向量慢。
特点
- 可以高效地进行任意位置的元素插入和删除操作。
- 支持在链表头和链表尾进行元素插入和删除操作。
- 不需要进行内存重分配。
(1)基本特性
- 列表的底层使用线性双向链表来实现
- 列表在任意位置插入和删除的操作都是常数时间
- 列表不支持随机访问 ( [ ] 和 at )
(2)使用方法
list<元素类型> list对象;
(3)成员函数
begin/end/rbegin/rend // 迭代器操作
front/push_front/pop_front // 头部操作
back/push_back/pop_back // 尾部操作
insert/erase // 指定位置插入和删除
size/resize/clear/empty // 大小操作
// 删除匹配的元素
void remove(const value_type &value);
// 排序
void sort();
void sort(比较器); // 传入两个元素获得比较结果(布尔值)----- 函数/函数对象
// 将列表中的指定(全部)元素转移到另一个列表中
void splice(const_iterator pos, list &other);
void splice(const_iterator pos, list& other, const_iterator first, const_iterator last);
// 归并二个已排序链表为一个
void merge(list& other);
void merge(list& other, Compare comp); // 比较器
// 删除连续的重复元素
void unique();)
forward_list(单向链表)
概念
C++ STL 中的forward_list
是一个单向链表容器,它的底层实现采用的是一个单向链表结构。
特点
- 插入和删除元素的时间复杂度是 O(1)
- 不需要进行内存重分配
- 空间利用率高:由于 forward_list 是一个单向链表,不需要额外的空间来存储指向前一个节点的指针。因此,在元素数量较大的情况下,forward_list 的空间利用率要比双向链表等其他容器高。
(1)基本特性
- 和list基本一致
- 唯一的区别底层实现使用单链表,单向访问。
(2)成员函数
forward_list只提供正向迭代器,成员函数之后begin/end,没有rbegin/rend
forward_list不提供尾部访问的接口,没有back/push_back/pop_back
forward_list不提供size成员函数
// forward_list只维护一个方向的链表,效率略高于list。
适配器容器
适配器容器包括栈,队列和优先队列,这些容器是在线性容器的基础上封装而成的,STL中的适配器容器需要提供一个底层的线性容器来实例化。
stack(栈)
概念
- C++ STL 中的
stack
是一个基于其他容器的适配器容器,它将底层容器封装起来,提供了栈操作的接口。因此,stack 的底层实现取决于它所使用的底层容器。 - 堆栈是先进后出的数据结构,只允许在一端(栈顶)访问,可以使用vector,deque(默认),list作为底层容器类型。
特点
- 使用方便:由于 stack 提供了栈操作的接口,因此可以方便地对栈进行操作,包括入栈、出栈、查看栈顶元素等操作。
- 实现简单:由于 stack 是一个适配器容器,它的底层容器可以是 deque、vector 或 list 等任意一个容器,因此实现起来比较简单。
- 时间复杂度低:由于 stack 使用了底层容器的 push_back 和 pop_back 操作来实现入栈和出栈,这些操作的时间复杂度都是 O(1),因此 stack 的操作比较高效。
- 支持自定义底层容器:由于 stack 是一个适配器容器,因此可以将任意一个容器作为它的底层容器,从而实现不同的功能。
(1)使用方法
stack<元素类型, vector<元素类型>> stack对象;
stack<元素类型> stack对象; // deque
(2)成员函数
入栈/压栈: push -----> push_back
出栈/弹栈: pop -----> pop_back
获取栈顶元素: top -----> back
获取元素个数: size -----> size
判空: empty -----> empty
(3)应用(计算中缀表达式)
queue(队列)
概念
C++ STL 中的queue
也是一个基于其他容器的适配器容器,它将底层容器封装起来,提供了队列操作的接口。队列是先进先出的数据结构,底层容器只能是deque和list,默认使用deque。
特点
- 使用方便:由于 queue 提供了队列操作的接口,包括入队、出队、查看队首元素等操作,因此可以方便地对队列进行操作。
- 实现简单:由于 queue 是一个适配器容器,它的底层容器可以是 deque 或 list 等任意一个容器,因此实现起来比较简单。
- 时间复杂度低:由于 queue 使用了底层容器的 push_back 和 pop_front 操作来实现入队和出队,这些操作的时间复杂度都是 O(1),因此 queue 的操作比较高效。
- 支持自定义底层容器:由于 queue 是一个适配器容器,因此可以将任意一个容器作为它的底层容器,从而实现不同的功能。
(1)使用方法
queue<元素类型, 底层容器类型=deque> 队列对象;
比如: queue<int, list<int>> qi;
(2)成员函数
入队: push -----> push_back
出队: pop -----> pop_back
获取队首元素: front -----> front
获取队尾元素: back -----> back
获取元素个数: size -----> size
判空: empty -----> empty
priority_queue(优先队列)
概念
C++ STL 中的priority_queue
也是一个基于其他容器的适配器容器,它将底层容器封装起来,提供了优先队列操作的接口。与 queue 不同的是,priority_queue 并不支持在任意位置插入元素,而是按照一定的优先级规则将元素插入到队列中。
特点
- priority_queue 的底层实现一般采用堆来实现,可以是大根堆或小根堆。大根堆中,每个节点的值都大于或等于其子节点的值,而小根堆中,每个节点的值都小于或等于其子节点的值。
- 时间复杂度低:由于 priority_queue 使用了堆来实现优先队列,堆的插入和删除操作的时间复杂度都是 O(log n),因此 priority_queue 的操作比较高效。
(1)使用方法
priority_queue<元素类型, 底层容器类型=vector> 优先队列对象;
比如: priority_queue<int, deque<int>> pi;
// 元素类型必须支持"<"运算符,以大者为先(重载函数必须加const)
priority_queue<元素类型, 底层容器类型=vector, 比较器类型> 优先队列对象;
// 使用比较器比较大小,以大者为先
(2)成员函数
入队: push
出队: pop
判空: empty
获取最优元素: top
(3)实现原理
- 使用了选择排序的方法,利用比较器每次选出一个最大的即可。
- 简单选择排序:两两比较选出一个最大的,和第一个元素交换这种方法效率一般。
- 优先队列使用的是堆排序的方法 ----- 构造二叉堆(大根堆)。
关联容器
关联容器内部使用平衡二叉树来实现,一共有4种:映射(map),多重映射(multimap),集合(set),多重集合(multiset)。
map(映射)
概念
C++ STL 中的map
是一种关联容器,它将键值对作为元素存储在容器中,并根据键的大小自动进行排序。map 中的每个元素都是一个键值对,键值对中的键是唯一的,而值可以重复。map 提供了插入、删除、查找等操作,同时还支持按照键的大小范围进行查询。
特点
- map 的底层实现一般采用红黑树来实现,红黑树是一种自平衡二叉搜索树,可以保证所有操作的时间复杂度为 O(log n)。
- 快速查找:由于 map 中的元素是按照键的大小自动排序的,因此可以快速进行查找操作,时间复杂度为 O(log n)。
- 支持动态扩展:map 支持动态插入、删除元素,因此可以根据需要进行动态扩展,而不需要预先知道容器的大小。
- 支持自定义排序规则:map 支持自定义比较函数,因此可以根据不同的排序规则来实现不同的功能。
- 可以作为字典使用:由于 map 中的元素是键值对,因此可以将它用作字典,可以根据键值对中的键来查找值。
(1)基本特性
- 映射支持顺序迭代,顺序迭代采取中序遍历,得到的是key的有序序列。
- key的比较默认使用"<"运算符,也可以提供比较器。
- 映射中的元素以pair为单位访问,pair有两个成员。
- 一个first对应key,second对应value
- 支持以key作为下标的运算,得到的是这个key对应的value。
(2)使用方法
map<key类型, value类型> 映射对象; // key必须支持"<"
map<key类型, value类型, 比较器类型> 映射对象;
(3)成员函数
// 构造pair对象的方法
使用pair的构造函数
pair<f_type, s_type> 对象名(xxx, yyy);
使用make_pair来构造
pair<f_type, s_type> make_pair(xxx, yyy );
// 迭代器: begin/end
// 插入
pair<iterator, bool> insert(const pair<key_type, value_type> &pa);
// 查找
iterator find(const key_type &key);
multimap(多重映射)
概念
C++ STL 中的multimap
是一种关联容器,它和 map 类似,都是将键值对作为元素存储在容器中,并根据键的大小自动进行排序。不同之处在于,multimap 中的键可以重复,因此可以存储多个具有相同键的值。
特点
- multimap 的底层实现一般也采用红黑树来实现,和 map 相同,可以保证所有操作的时间复杂度为 O(log n)。
- 可以存储具有相同键的多个值:由于 multimap 中的键可以重复,因此可以存储多个具有相同键的值,这是 map 所不能做到的。
- 不支持关于key的下标运算
- 其他同map一样
(1)成员函数
// 获取匹配的上限 -----> 返回首个大于给定key的迭代器
iterator upper_bound (const key_value &key);
// 获取匹配的下限 -----> 返回首个不小于给定key的迭代器
iterator lower_bound(const key_value &key);
// 同时获取上下线
pair<iterator, iterator> equal_range(const key_value &key);
A - 100
B - 200 --- B的下限
B - 300
B - 400
C - 500 --- B的上限
set(集合)
概念
C++ STL 中的set
是一种关联容器,它可以用来存储一些没有重复元素的数据。set 中的元素是自动排序的,由于使用了红黑树作为底层实现,因此可以保证所有操作的时间复杂度为 O(log n)。集合就是没有value的映射。
特点
- 自动排序:set 中的元素是自动排序的,可以按照指定的排序规则进行排序,这样就可以方便地进行查找操作。
- 不重复元素:set 中的元素是不重复的,因此可以保证每个元素的唯一性。
- 高效的查找:由于 set 中的元素是自动排序的,因此可以快速进行查找操作,时间复杂度为 O(log n)。
- 支持动态扩展:set 支持动态插入、删除元素,因此可以根据需要进行动态扩展,而不需要预先知道容器的大小。
multiset(多重集合)
概念
C++ STL 中的multiset
与 set 类似,也是一种关联容器,但是可以存储重复的元素。multiset 与 set 一样,采用红黑树作为底层实现,因此可以保证所有操作的时间复杂度为 O(log n)。
特点
- 支持重复元素:multiset 支持存储重复的元素,可以方便地存储相同的元素。
- 其他与set一样