C++ STL 序列式容器 array/vector/deque/list

序列式容器,即以线性排列(类似普通数组的存储方式)来存储某一指定类型(例如 int、double 等)的数据,需要特殊说明的是,该类容器并不会自动对存储的元素按照值的大小进行排序。序列式容器有array, vector, deque, list, forward_list。

  • array,静态数组容器,此类容器一旦建立,其长度就固定不变,即不能增加或删除元素,而只能改变某个元素的值;可直接访问某一元素

  • vector,向量容器,是一个长度可变的容器,即存储空间不足时会自动申请更多内存,在尾部增加或删除元素的效率最高(时间复杂度为 O(1) 常数阶),在其它位置插入或删除元素效率较差(时间复杂度为 O(n) 线性阶,其中 n 为容器中元素的个数);可直接访问某一元素

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

  • list,双向链表容器,是一个长度可变的以双向链表的形式组织元素的容器,在这个序列的任何地方都可以高效地增加或删除元素(时间复杂度都为常数阶 O(1)),但访问容器中任意元素的速度要比前三种容器慢,这是因为 list 必须从第一个元素或最后一个元素开始访问,需要沿着链表移动,直到到达想要的元素

  • forward_list,单向链表容器,和list容器相似,只不过它以单链表的形式组织元素,它内部的元素只能从第一个元素开始访问,是一类比链表容器快、更节省内存的容器

序列式容器 array

array 容器是在 C++ 普通数组的基础上,添加了一些成员函数和全局函数。在使用上,它比普通数组更安全,且效率并没有因此变差。array 容器的大小是固定的,无法动态的扩展或收缩,这也就意味着,在使用该容器的过程无法借由增加或移除元素而改变其大小,它只允许访问或者替换存储的元素。

array 容器以类模板的形式定义在 <array> 头文件,并位于命名空间 std 中。

#include <array>
using namespace std;
//构造
array<int, 9> a;//定义具有9个int型元素的array容器

array<int, 9> a{};//定义具有9个int型元素的array容器并初始化
array<int, 8> a{1,2,3};//定义具有9个int型元素的array容器,并指定前三个元素,其余元素初始化为0
array<int, 8> a = {1,2,3};//同上

array<int, 9> a1(a);//复制构造
array<int, 9> a1 = a;//同上

array <array<int, 9>, 9> aa{};//定义二维数组并初始化各元素为0
//迭代器
a.begin(); //返回迭代器, 指向第一元素
a.end(); //返回迭代器, 指向最末元素的下一个位置
a.cbegin(); //返回迭代器, 指向第一元素, 类型为const
a.cend();//返回迭代器, 指向最末元素的下一个位置,类型为const
a.rbegin(); //返回反向迭代器, 指向反向迭代的第一元素
a.rend(); //返回反向迭代器, 指向反向迭代的最末元素的下一个位置
a.crbegin(); //返回反向迭代器, 指向反向迭代的第一元素, 类型为const
a.crend(); //返回反向迭代器, 指向反向迭代的最末元素的下一个位置, 类型为const

//批量赋值
a.assign(3);//将容器的每个元素都赋值成 3
a.fill(4);//将容器的每个元素都赋值成 4

//交换
a.swap(a1);//交换两个容器的内容
//例:a={1,2,3,4}, a1={5,6,7}
//运行之后, a={5,6,7}, a1={1,2,3,4}
    
//访问
a[1];//返回下标为 id 的元素, 不检查是否越界
a.at(1);//返回下标为 id 的元素, 如果越界抛出异常
a.front();//返回首位元素
a.back();//返回末位元素

//容量
a.empty();//容器为空返回true, 否则返回 false;即size=0时为true
a.size();//返回容器内目前的元素个数, array定义之后不变
a.max_size();//返回元素个数 size 的最大值;返回的值和a.size()相同.
//注: 由于array 是静态数组, 因此这里的 size 和 max_size 相同
//遍历
array<array<int, 4>, 5> a{};//假定a是二维数组,且维度为[5,4]
for(auto & it1 : a)// it1是array<int, 4>类型的引用
{
    for(int & it2 : it1)//it2是int类型的引用
    {
        cout << it2 << "  ";
    }
    cout << endl;
}

序列式容器 vector

vector 容器是 STL中最常用的容器之一,它和 array 容器非常类似,都可以看做是对 C++普通数组的“升级版”。不同之处在于,array 实现的是静态数组(容量固定的数组),而 vector 实现的是一个动态数组,即可以进行元素的插入和删除,在此过程中,vector 会动态调整所占用的内存空间,整个过程无需人工干预。

vector 常被称为向量容器,因为该容器擅长在尾部插入或删除元素,在常量时间内就可以完成,时间复杂度为O(1);而对于在容器头部或者中部插入或删除元素,则花费时间要长一些(移动元素需要耗费时间),时间复杂度为线性阶O(n)

vector的size和capacity,size是当前vector容器真实占用的大小(即容器内存储着多少数据),capacity是预分配的内存空间大小;分别对应resize()和reserve()方法,前者改变容器真实占用大小,后者改变预分配的内存空间大小。

#include<vector>
using namespace std;
//一些初始化方式
vector<int> a;//定义空的vector容器a
vector<int> a(10);//定义包含10个元素的容器a并默认初始化各个元素为0
vector<int> a(10, 1);//定义包含10个元素的容器a并初始化各个元素为1

vector<int> a{1,2,3,4};//定义容器a并初始化它
vector<int> a = {1,2,3,4};//同上

vector<int> a(b);//从同容器中复制构造
vector<int> a(b.begin(), b.begin()+3);//从其他容器中构造

int b[7] = {1, 2, 3, 4, 5, 6, 7};
vector<int> a(b, b+7);//从数组中构造

vector<vector<int>> v(4, vector<int>(5));//静态构造二维数组,维度为[4,5]
//迭代器
vec.begin();//得到vector头的指针
vec.end();//得到vector的最后一个单元+1的指针
vec.rbegin();//将vector反转后的开始指针返回(其实就是原来的end-1)
vec.rend();//将vector反转构的结束指针返回(其实就是原来的begin-1)
vec.cbegin();
vec.cend();
vec.crbegin();
vec.crend();

//容量
vec.size();//当前使用数据的大小
vec.max_size();//得到vector最大可以是多大/ 区别于vec.size()
vec.capacity();//当前vector分配的大小

vec.empty();//判断vector是否为空,空时返回true,即size为0时返回true
vec.resize();//改变当前使用数据的大小,如果它比当前使用的大,则填充默认值
vec.reserve();//改变当前vector所分配空间的大小
vec.shrink_to_fit();//将内存减少到等于当前元素实际使用的大小

//访问
vec[1];//得到1号位置的数据
vec.at(1);//得到1号位置的数据

vec.front();//返回首位元素的引用
vec.back();//返回末位元素的引用
vec.data();//返回指向容器中第一个元素的指针

//修改
vec.assign(n, ele);//将vec赋值为n个ele元素
vec.assign(first, last);//将vec赋值为[first,last)之间的元素
vec.push_back();//在最后位置添加一个数据
vec.insert(it, ele);//在迭代器it指定的位置之前插入元素ele
vec.insert(it, n, ele);//在迭代器it指定的位置之前插入n个元素ele
vec.insert(it, first, last);//在迭代器it指定的位置之前,插入其他容器(不仅限于vector)中位于 [first,last) 区域的所有元素
vec.insert(it, {12, 34});//在迭代器 pos 指定的位置之前,插入初始化列表(用大括号{}括起来的多个元素,中间有逗号隔开)中所有的元素
vec.emplace(it, ele);//在迭代器it指定的位置之前插入元素ele
vec.emplace_back();//在末尾生成一个元素
vec.pop_back();//去掉最后一个数据
vec.erase(it);//删除迭代器it指向的元素
vec.erase(beg, end);//删除 vector 容器中位于迭代器 [beg,end)指定区域内的所有元素
vec.clear();//删除 vector 容器中所有的元素

vec.swap(vec1);//来自于algorithm的函数,与另一个vector容器vec1交换数据
remove(vec.begin(), vec.end(), ele);//来自于algorithm的函数,可删除容器中所有和指定元素值相等的元素,注意删除后容器大小不变,容量也不变

序列式容器 deque

deque是一个double-ended queue,又称双端队列容器。deque在序列尾部和头部添加和删除元素的时间复杂度是O(1)(vector容器只擅长在序列尾部添加和删除元素),在中间添加和删除元素的时间复杂度要更高。deque也是一个动态容器,可以根据需要修改自身容量和大小。与vector容器不同,deque 容器中存储元素并不能保证所有元素都存储到连续的内存空间中。

#include <queue>
using namespace std;
deque<int> d;//创建一个空deque
deque<int> d(nSize);//创建一个deque,元素个数为nSize,且各个元素初始化为默认值
deque<int> d(nSize, t);//创建一个deque,元素个数为nSize,且值均为t

deque<int> d{1,2,3,4,5};//创建并初始化一个deque
deque<int> d = {1,2,3,4,5};//同上

//也可从数组中构造,从其他容器中构造,或者复制构造

用法参考 vector 容器,

和 vector 相比,额外增加了实现在容器头部添加和删除元素的成员函数,同时删除了 capacity()、reserve() 和 data() 成员函数。

//迭代器
//容量
d.size();
d.max_size();
d.resize(n);//将deque的长度改为n,超出的元素将被删除
d.empty();//deque为空返回true,否则返回false
d.shrink_to_fit();//将内存减少到等于当前元素实际所使用的大小

//访问
d.at(1);
d[1];
d.front();//获取deque头部元素的引用
d.back();//获取deque最后一个元素的引用

//修改
d.assign();
d.push_back();
d.push_front();
d.emplace_back();
d.emplace_front();
d.insert();
d.emplace();

d.pop_back();
d.pop_front();
d.erase();
d.clear();

序列式容器 list

STL list 容器,又称双向链表容器,即该容器的底层是以双向链表的形式实现的。这意味着,list 容器中的元素可以分散存储在内存空间里,而不是必须存储在一整块连续的内存空间中。

list 容器中各个元素的前后顺序是靠指针来维系的,每个元素都配备了 2 个指针,分别指向它的前一个元素和后一个元素。其中第一个元素的前向指针总为 null,因为它前面没有元素;同样,尾部元素的后向指针也总为 null。

基于这样的存储结构,list 容器具有一些其它容器(array、vector 和 deque)所不具备的优势,即它可以在序列已知的任何位置快速插入或删除元素(时间复杂度为O(1))。并且在 list 容器中移动元素,也比其它容器的效率高。使用 list 容器的缺点是,它不能像 array 和 vector 那样,通过位置直接访问元素。

list 容器在<list>头文件中,并位于 std 命名空间中。

#include <list>
using namespace std;
list<int> l;//声明一个空列表;
list<int> l(n);//声明一个有n个元素的列表,每个元素都是由其默认构造函数T()构造出来的
list<int> l(n,val);//声明一个由n个元素的列表,每个元素的值都是val得来的
 
list<int> l{1,2,3};//声明并初始化一个列表

//也可以从数组中构造,从其他容器中构造,或者复制构造

序列式容器 forward_list

forward_list 是 C++ 11 新添加的一类容器,其底层实现和 list 容器一样,采用的也是链表结构,只不过 forward_list 使用的是单链表,而 list 使用的是双向链表。

forward_list 容器具有和 list 容器相同的特性,即擅长在序列的任何位置进行插入元素或删除元素的操作,但对于访问存储的元素,没有其它容器(如 array、vector)的效率高。另外,由于单链表没有双向链表那样灵活,因此相比 list 容器,forward_list 容器的功能受到了很多限制。比如,由于单链表只能从前向后遍历,而不支持反向遍历,因此 forward_list 容器只提供前向迭代器,而不是双向迭代器。这意味着,forward_list 容器不具有 rbegin()、rend() 之类的成员函数。

forward_list 容器底层使用单链表,存储相同个数的同类型元素,单链表耗用的内存空间更少,空间利用率更高,并且对于实现某些操作单链表的执行效率也更高。

forward_list 容器包含在<forward_list>头文件中,并定义在 std 命名空间中。

#include <forward_list>
using namespace std;

forward_list<int> fl;//创建一个没有任何元素的空 forward_list 容器
forward_list<int> fl(n);//创建一个包含 n 个元素的 forward_list 容器,并且各个元素被默认初始化
forward_list<int> fl(n, value);//创建一个包含 n 个元素的 forward_list 容器,并为每个元素指定初始值

forward_list<int> fl{1,2,3};//创建并初始化
forward_list<int> fl = {1,2,3};//同上

//从数组中构造,从其他容器中构造,或者复制构造
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值