C++中常见的三种序列式容器vector、list、deque
前言
C++中标准的数组使用起来确实很方便,随机访问也很快。但是有一个致命的缺点:由于数组是一组固定的连续空间,所以不允许删除和添加元素。在实际的应用场景中,会显得有些捉襟见肘。STL中提供了很多不同的结构容器来弥补传统数组的不足,这里主要介绍常用的三种序列式容器。
vector
向量vector是最常用的一种,对比数组,它允许元素的插入和删除操作。事实上,vector底层逻辑也是数组,它的空间是连续的。它会预留一部分的空间来供元素插入,当需要插入的元素比较多时,就会重新申请一块较大的空间,然后把整个复制过去,最后释放原空间。而当删除元素时,原来的空间也不会得到释放,仍然会作为容量的一部分。
由于空间的连续性,因此,在执行插入操作时,越靠后需要移动的元素越少,执行插入的效率越高,删除同理。反之,在头部执行插入和删除的效率就会异常地低,如果需要大量的插入新元素,或者需要频繁的操作头部的数据,那么vector可能不是最好的选择。
因为空间的连续性,所以vector支持随机访问(下标访问)且效率很高,同时,它的迭代器支持it+n操作。
vector声明和初始化
要使用vector需要包含头文件<vector>,基本语法为:
vector<typename> variblename(constructor);
例如:
//声明一个存储int类型数据的向量 vector<int> v; //声明一个长度为5(注意并非最大容量为5)的向量,并全部初始化为0 vector<int> v1(5); //声明一个长度为5的向量,并全部初始化为2 vector<int> v2(5,2); //声明一个向量,并初始化元素1,2,3 vector<int> v3{1,2,3};
常用方法
v.capacity(); //容器容量 v.size(); //容器大小 v.at(int idx); //用法和[]运算符相同 v.push_back(); //尾部插入 v.pop_back(); //尾部删除 v.front(); //获取头部元素 v.back(); //获取尾部元素 v.begin(); //头元素的迭代器 v.end(); //尾部元素的迭代器 v.insert(pos,elem); //pos是vector的插入元素的位置 v.insert(pos, n, elem) //在位置pos上插入n个元素elem v.insert(pos, begin, end);//在pos位置插入在[beg,end)区间的数据。 v.erase(pos); //移除pos位置上的元素,返回下一个数据的位置 v.erase(begin, end); //移除[begin, end)区间的数据,返回下一个元素的位置 v.reverse(pos1, pos2); //将vector中的pos1~pos2的元素逆序存储
list
list底层是一个双向链表,因此它也具有链表的特性,即高效的插入和删除操作,但是访问效率较低,且不支持随机访问,也无法使用下标[]来访问元素。它的迭代器只能进行自增或自减,无法使用it+n操作,因为无法预测节点的位置。
list的每一个节点都有三个部分组成:前驱指针域,数据域和后继指针域。这也时双向链表的显著特征。
前驱指针域存储前一个节点的位置,数据域保存本节点的数据,后继指针域保存后继节点的位置。每一个节点在内存中的空间并不连续,节点与节点之间完全靠指针联系,无法跨节点访问,属于逻辑意义上的线性表。
deque
deque的全称是double-ended queue 即双端队列。它是一个双开口的分段连续的线性空间,兼具链表和数组的部分特性。它是由一段一段的连续空间连起来的,每一段空间的首地址会单独存放在一个数组中。因此,deque内部的结构分为分段数组和索引数组, 分段数组是存储数据的,索引数组是存储每段数组的首地址的。
deque的内部是分段的动态连续空间。当需要执行插入时,会先尝试在两端的段内空间进行操作,如果不足时,就会在deque的头部或者尾部配置一段新的空间。所以向两端插入元素效率较高,而中间插入元素效率较低(因为需要对索引数组进行维护)。而deque通过索引数组这个中控器和复杂的迭代器来维持整体连续的假象,因此,在使用时,deque也表现出连续空间容器的特征:我们可以使用下标来进行随机访问,同时,迭代器也支持it+n操作。
关于list和deque的使用,大致语法和vector差不多,这里就不再赘述了。另行按需查阅资料即可。
特别的,以deque为例,一个二维结构的创建方式:
#include <iostream> #include <deque> using namespace std; int main(){ //创建一个一维deque,初始化三个元素1,2,3 deque<int> deque1{1,2,3}; //一个外部deque,里面初始化5个deque1,形成二维结构 deque<deque<int>> deque2(5,deque1); //遍历 for(deque<int> i:deque2){ for(int j:i){ cout<<j<<endl; } } }