1-STL(上)
STL容器
在我们写算法的过程中,常常会使用到链表,集合等常见的数据结构,但是如果我们每次都要自己实现它的话,无疑大大浪费了我们的时间。
这个时候STL就来了,它就是一些模板类的集合,容器中封装的是组织数据的方法(数据结构),它分为以下类别:
| 种类 | 功能 |
|---|---|
| 序列容器 | 主要包括 vector 向量容器、list 列表容器以及 deque 双端队列容器。之所以被称为序列容器,是因为元素在容器中的位置同元素的值无关,即容器不是排序的。将元素插入容器时,指定在什么位置,元素就会位于什么位置。 |
| 排序容器 | 包括 set 集合容器、multiset多重集合容器、map映射容器以及 multimap 多重映射容器。排序容器中的元素默认是由小到大排序好的,即便是插入元素,元素也会插入到适当位置,关联容器在查找时具有非常好的性能。 |
| 哈希容器 | C++ 11 新加入的 4 种关联式容器,分别是 unordered_set 哈希集合、unordered_multiset 哈希多重集合、unordered_map 哈希映射以及 unordered_multimap 哈希多重映射。和排序容器不同,哈希容器中的元素是未排序的,元素的位置由哈希函数确定。 |
迭代器
上述提到的容器,最常做的操作无疑是遍历容器中存储的元素,那么我们遍历的元素具有很多种类型,我们需要用泛型技术将它们设计成所有容器都适用的通用算法,那么迭代器就是其中的中介。
-
前向迭代器 假设p是一个前向迭代器,那么它支持p++,++p,*p操作,同时还可以被复制或赋值,可以用==等比较符号进行比较。
-
双向迭代器 它具有前向的全部功能,同时它还能执行--p,p--等后向功能。
-
随机访问迭代器 它支持双向迭代器的所有功能,除此之外,假设i为一个整型变量或者constant,那么它可以移动i个前后位置。
注:我们可以注意到一直到双向迭代器,是不支持<,>这种比较符进行比较的。
序列容器
序列容器就是以线性排列(类似普通数组的存储方式)来存储某一指定类型(例如 int、double 等)的数据。
- array (数组容器):表示可以存储 N 个 T 类型的元素,是C++本身提供的一种容器。此类容器一旦建立,其长度就是固定不变的,这意味着不能增加或删除元素,只能改变某个元素的值;(所以用的不多)
- vector(向量容器):用来存放 T 类型的元素,是一个长度可变的序列容器,在存储空间不足时,会自动申请更多的内存。使用此容器,在尾部增加或删除元素的效率最高(时间复杂度为 O(1) 常数阶),在其它位置插入或删除元素效率较差(时间复杂度为 O(n) 线性阶,其中 n 为容器中元素的个数);(可以看出是很像一个数组的)
- deque(双端队列容器):和 vector 非常相似,区别在于使用该容器不仅尾部插入和删除元素高效,在头部插入或删除元素也同样高效,时间复杂度都是 O(1) 常数阶,但是在容器中某一位置处插入或删除元素,时间复杂度为 O(n) 线性阶;
- list(链表容器):是一个长度可变的、由 T 类型元素组成的序列,它以双向链表的形式组织元素,在这个序列的任何地方都可以高效地增加或删除元素(时间复杂度都为常数阶 O(1)),但访问容器中任意元素的速度要比前三种容器慢,这是因为 list 必须从第一个元素或最后一个元素开始访问,需要沿着链表移动,直到到达想要的元素。
- forward_list(正向链表容器):和 list 容器非常类似,只不过它以单链表的形式组织元素,它内部的元素只能从第一个元素开始访问,是一类比链表容器快、更节省内存的容器。
其实也可以注意到,有些容器用的很多,但有些容器我们基本上不怎么使用,首先来讲讲序列容器的主要使用的内置方法。
vector
vector<int> v;//初始化一个int型容器
v.begin(),v.end();//显而易见返回第一个元素和最后一个元素位置
v.size();//定义长度时常使用
v.empty();//判断容器中是否有元素
v.resize();//改变实际元素的个数
v.front(),v.back();//返回第一个和最后一个元素的引用
v.push_back();//用的最多,在序列尾部添加一个元素
v.pop_back();//移除序列尾部的元素
v.erase();//移除一个元素或一段元素
v.emplace();//在指定位置生成一个元素
v.emplace_back();//在序列尾部生成一个元素
注:deque无非就是增加了能在结构的头部添加数据,但是vector的方法是包含了deque的,所以没有要在存储头部添加数据需求时使用vector即可
同时对于(C++ 11 new function)emplace()类的成员函数,相比和它同功能的函数,emplace 系列函数都只调用了构造函数,而没有调用移动构造函数,这无疑提高了代码的运行效率。push_back() 在底层实现时,会优先选择调用移动构造函数,如果没有才会调用拷贝构造函数。
显然完成同样的操作,push_back() 的底层实现过程比 emplace_back() 更繁琐,换句话说,emplace_back() 的执行效率比 push_back() 高。因此,在实际使用时,建议大家优先选用 emplace_back()
list
list<int> l;
l.begin(),l.end();//与vector相同
l.assign();//用新元素替换掉原有内容
//注意到大部分vector能用的我们list都能用!
l.empty();//判断容器中是否有元素
l.resize();//改变实际元素的个数
l.front(),l.back();//返回第一个和最后一个元素的引用
l.push_back();//用的最多,在序列尾部添加一个元素
l.pop_back();//移除序列尾部的元素
l.erase();//移除一个元素或一段元素
l.emplace();//在指定位置生成一个元素
l.emplace_back();//在序列尾部生成一个元素
new
l.push_front();//表示起始位置新添一个元素
l.pop_front();//移除序列头部元素
l.reverse();//反转容器某一段元素
l.unique();//移除所有连续重复的元素
l.remove();//移除所有与参数匹配的元素
l.sort();//sort哪都有,会用就行
l.merge();//合并两个有序容器
要突出声明一个list的函数:splice(),splice() 成员方法的作用对象是其它 list 容器,其功能是将其它 list 容器中的元素添加到当前 list 容器中指定位置处。
| 语法格式 | 功能 |
|---|---|
| void splice (iterator position, list& a); | position 为迭代器,用于指明插入位置;x 为另一个 list 容器。此格式的 splice() 方法的功能是,将 a 容器中存储的所有元素全部移动当前 list 容器中 position 指明的位置处。 |
| void splice (iterator position, list& a, iterator i); | position 为迭代器,用于指明插入位置;a为另一个 list 容器;i 也是一个迭代器,用于指向a容器中某个元素。此格式的 splice() 方法的功能是将a容器中 i 指向的元素移动到当前容器中 position 指明的位置处。 |
| void splice (iterator position, list& a, iterator first, iterator last); | position 为迭代器,用于指明插入位置;a为另一个 list 容器;first 和 last 都是迭代器,[fist,last) 用于指定 a 容器中的某个区域。此格式的 splice() 方法的功能是将 a 容器 [first, last) 范围内所有的元素移动到当前容器 position 指明的位置处。 |
也就是这个方法可以将iterator指向某一元素,然后将其他的元素插入到这一元素的后面,这对某些算法的具体实现会有帮助。
注:forward_list 容器具有和 list 容器相同的特性,即擅长在序列的任何位置进行插入元素或删除元素的操作,但对于访问存储的元素,没有其它容器(如 array、vector)的效率高。
另外,由于单链表没有双向链表那样灵活,因此相比 list 容器,forward_list 容器的功能受到了很多限制。比如,由于单链表只能从前向后遍历,而不支持反向遍历,因此 forward_list 容器只提供前向迭代器,而不是双向迭代器。这意味着,forward_list 容器不具有 rbegin()、rend() 之类的成员函数。
效率高是选用 forward_list 而弃用 list 容器最主要的原因,换句话说,只要是 list 容器和 forward_list 容器都能实现的操作,应优先选择 forward_list 容器。

浙公网安备 33010602011771号