C++(9):顺序容器

news/2024/11/19 17:34:14/

顺序容器概述

所有顺序容器都提供了快速顺序访问元素的能力。

vector//可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢
deque//双端队列。支持快速随机访问。在头尾位置插入/删除速度很快
list//双向链表。只支持双向顺序访问。在list中任何位置进行插入/删除操作速度都很快
forward_list//单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快
array//固定大小数组。支持快速随机访问。不能添加或删除元素
string//与vector 相似的容器,但专门用于保存字符。随机访问快。在尾部插入/删除速度快

string 和 vector 将元素保存在连续的内存空间中。
由于元素是连续存储的,由元素的下标来计算其地址是非常快速的。但是,在这两种容器的中间位置添加或删除元素就会非常耗时:在一次插入或删除操作后,需要移动插入/删除位置之后的所有元素,来保持连续存储。而且,添加一个元素有时可能还需要分配额外的存储空间。在这种情况下,每个元素都必须移动到新的存储空间中。

list 和 forward list 两个容器任何位置的添加和删除操作都很快速。
作为代价,这两个容器不支持元素的随机访问:为了访问一个元素,我们只能遍历整个容器。而且,与vector、deque和 array相比,这两个容器的额外内存开销也很大。

deque 的中间位置添加或删除元素的代价很高,但在两端添加或删除元素很快。

array对象大小是固定的,不支持添加和删除元素以及改变容器大小的操作。

forward_list 没有 size 操作,forward_list 的设计目标是达到与最好的手写的单向链表数据结构相当的性能,保存或计算 forward_list 大小就会比手写链表多出额外的开销。

容器选择:

  • 没有特殊的理由,使用 vector 是最好的选择;
  • 有很多小的元素,不用 list 或 forward_list;
  • 随机访问元素,用 vector 和 deque;
  • 在容器中间插入或删除元素,用 list 和 forward_list;
  • 在头尾位置插入或删除元素,但不在中间位置插入或删除元素,用deque;
  • 在中间位置插入,而后随机访问:
    ①向 vector 中追加数据,再调用 sort 函数来排序;
    ②在输入阶段用 list ,输入完成后拷贝到 vector 中。

容器库概览

在这里插入图片描述
构造函数:同类拷贝、迭代器范围初始化、列表初始化

C c;//默认构造函数,构造空容器
C c1(c2);// 拷贝构造函数,容器类型与元素类型必须都相同,c2->c1
C c1(c2.begin(),c2.end());// 要求两种元素类型可以转换即可。
C c1{a,b,c,...};// 使用初始化列表,容器的大小与初始化列表一样大

赋值与 swap

c1=c2;//将c1中的元素替换为c2中的元素
c1={a,b,c...};//将c1中的元素替换为列表中的元素(不适用于 array)
a.swap(b);//交换a和b的元素
swap(a,b);//与a.swap(b)等价

大小

c.size();//c中元素的数目(不支持forward_list)
c.max_size();//c可保存的最大元素数目
c.empty();//若c中存储了元素,返回false,否则返回true

添加/删除元素

c.insert(args);    // 将 args 中的元素拷贝进 c
c.emplace(inits);  // 使用 inits 构造 c 中的一个元素
c.erase(args);     // 删除指定的元素
c.clear();

反向容器的额外成员
在这里插入图片描述

迭代器

迭代器范围由一对迭代器表示,两个迭代器表示的范围都是左闭合空间,即 [begin,end) :如果 begin 和 end 相等,则为空。
所有迭代器都可以递增,forward_list 不可以递减

// iter是通过list<string>定义的一个迭代器类型
list<string>::iterator iter;
//count是通过vector<int>定义的一个difference_type类型
vector<int>::difference_type count;

当不需要写访问时,应使用 cbegin和cend。加c后的版本是 const_iterator。

容器定义和初始化

当将一个容器初始化为另一个容器的拷贝时,两个容器的容器类型和元素类型都必须相同。

只有顺序容器的构造函数才接受大小参数,关联容器并不支持。

对 array 进行列表初始化,初始值的数目必须等于或小于 array 的大小。

array<int, 10>ial;//10个默认初始化的int
array<int,10> ia2 = {0,1,2,3,4,5,6,7,8,9};//列表初始化
array<int, 10>ia3 = (42); // ia3[0]为42,剩余元素为0

如果定义的 array 很大并且需要初始化,可以先默认初始化然后用 fill 函数填充值。
array 可以拷贝或赋值,使用一个 array 对另一个 array 赋值,需要两个array 元素类型与大小都相同。

赋值和 swap

在这里插入图片描述“=”赋值
对容器使用赋值运算符(除 array 外),将会使该容器的所有元素被替换。如果两个容器大小不等,赋值后都与右边容器的原大小相同。
array要求赋值前大小就必须一样。

assign:允许从一个不同但相容的类型赋值,或者从容器的一个子序列赋值。

list<string> names;
vector<const char*>oldstyle;
names = oldstyle;//错误:容器类型不匹配
//正确:可以将 const char* 转换为 string
names.assign (oldstyle.cbegin(),oldstyle.cend());

swap:除array外,交换两个容器内容的操作保证会很快—元素本身并未交换,swap 只是交换了两个容器的内部数据结构。

顺序容器操作

添加元素

在这里插入图片描述
向 vector、string 或 deque 插入元素会使所有指向容器的迭代器、引用和指针失效。
emplace函数在容器中直接构造元素。传递给emplace函数的参数必须与元素类型的构造函数相匹配。
emplace_front、 emplace 和 emplace_back 操作构造而不是拷贝元素。

访问元素

访问元素首先保证容器是非空的。

if(!c.empty()){}

在这里插入图片描述

删除元素

删除元素之前,首先要保证它是存在的。

在这里插入图片描述

特殊的 forward_list 操作

forward_list 是单向链表,添加和删除操作都会同时改变前驱和后继结点,因此一般的添加和删除都不适用于 forward_list。
在这里插入图片描述

改变容器大小

容器操作可能使迭代器失效

添加元素后:

  • 如果容器是vector或string,且存储空间被重新分配,则指向容器的迭代器、指针和引用都会失效。如果存储空间未重新分配,指向插入位置之前的元素的迭代器、指针和引用仍有效,但指向插入位置之后元素的迭代器、指针和引用将会失效。
  • 对于 deque,插入到除首尾位置之外的任何位置都会导致迭代器、指针和引用失效。如果在首尾位置添加元素,迭代器会失效,但指向存在的元素的引用和指针不会失效。
  • 对于 list和forward_list,指向容器的迭代器(包括尾后迭代器和首前迭代器)、指针和引用仍有效。

删除元素后:

  • 对于list和 forward list,指向容器其他位置的迭代器(包括尾后迭代器和首前迭代器)、引用和指针仍有效。
  • 对于 deque,如果在首尾之外的任何位置删除元素,那么指向被删除元素外其他元素的迭代器、引用或指针也会失效。如果是删除 deque 的尾元素,则尾后迭代器也会失效,但其他迭代器、引用和指针不受影响;如果是删除首元素,这些也不会受影响。
  • 对于vector和string,指向被删元素之前元素的迭代器、引用和指针仍有效。注意:当我们删除元素时,尾后迭代器总是会失效。

不要保存 end 返回的迭代器:push、pop、首尾 emplace 操作都没有返回值,但是都会改变尾后迭代器。

保证每次改变容器的操作之后都正确地重新定位迭代器

vector 对象

为了支持快速访问,vector 将元素连续存储。
为了避免每增加一个元素就要重新分配一次空间,在每次必须获取新的内存空间时,通常会分配比新的空间需求更大的内存空间。容器预留多的空间作为备用。

容量管理:
在这里插入图片描述
容器的size是指它已经保存的元素的数目;而capacity则是在不分配新的内存空间的前提下它最多可以保存多少元素。
c.reserve(n) 不会减小容量,只会增大容量,当需求容量大于当前容量,才会分配内存,否则什么都不做。
c.shrink_to_fit() 只是一个请求,实现时标准库可能会不执行。

额外的 string 操作

1.提供string类和C风格字符数组之间的相互转换;
2.允许用下标代替迭代器。

构造 string 的其他方法

//n, len2和pos2都是无符号值
string s(cp,n)//s是cp指向的数组中前n个字符的铂贝。此数组至少应该包含n个字符
string s (s2, pos2)//s是 string s2从下标 pos2开始的字符的铂贝。若pos2>s2.size(),构造函数的行为未定义
string s(s2,pos2,len2)//s是string s2从下标pos2开始len2个字符的拷贝。若pos2>s2.size (),构造函数的行为未定义。不管1en2的值是多少,构造函数至多拷贝s2.size ()-pos2个字符

substr 操作
substr操作返回一个string,它是原始string 的一部分或全部的拷贝。可以传递给substr一个可选的开始位置和计数值:

s.substr(pos, n)//返回一个string,包含s中从pos 开始的n个字符的铂贝。pos的默认值为0。n的默认值为s.size()-pos,即铂贝从pos 开始的所有字符

改变 string 的其他方法

在这里插入图片描述

string 搜索操作

string 搜索函数返回 string:: size_typ e值,该类型是一个 unsigned 类型。
在这里插入图片描述在这里插入图片描述

注意:find 和 rfind 查找的是给定的整个 args,而剩下的查找的是给定的 args 中包含的任意一个字符。

compare 函数

用于比较两个字符串,可以是比较整个或一部分字符串。
小于返回负数,大于返回正数,等于返回零。

int F = s.compare(s2);
int F = s.compare(pos1,n1,s2);          // 将 s 中 pos1 开始的前 n1 个字符与 s2 比较
int F = s.compare(pos1,n1,s2,pos2,n2);  // 将 s 中 pos1 开始的前 n1 个字符与 s2 中从 pos2 开始的 n2 个字符进行比较
int F = s.compare(cp)                   // 将 s 与 cp 指向的字符数组比较
int F = s.compare(pos1,n1,cp);
int F = s.compare(pos1,n1,cp,n2);

数值转换

string 与 数值之间的转换:
在这里插入图片描述

容器适配器

标准库定义了三个顺序容器适配器:stack、queue、priority_queue。
适配器是一种机制,是某种事物的行为看起来像另一种事物。

适配器类型
在这里插入图片描述
定义一个适配器
每个适配器都定义两个构造函数:默认构造函数创建一个空对象,接受一个容器的构造函数拷贝该容器来初始化适配器。

stack<int> stk(deq);//从deq拷贝元素到stk

默认情况下, stack 和 queue 是基于 deque 实现的, priority_queue 是在 vector 之上实现的。
在创建适配器时用一个顺序容器作为第二个类型参数,来重载默认容器类型:

//在vector上实现的空栈
stack<string, vector<string>> str_stk;
//str_stk2在vector上实现,初始化时保存svec的拷贝
stack<string, vector<string>> str_stk2(svec);

栈适配器

在这里插入图片描述
队列适配器
queue 和 priority_queue 都定义在头文件 queue 中。
queue 使用一种先进先出(first-in, first-out,FIFO)的存储和访问策略。队尾进,队首出。
priority_queue 允许为队列中的元素建立优先级。新加入的元素会排在所有优先级比它低的已有元素之前。

在这里插入图片描述在这里插入图片描述

重要术语

首前迭代器 :表示一个 forward_list 开始位置之前(不存在的)元素的迭代器。是 forward_list 的成员函数 befor_begin 的返回值。与 end() 迭代器类似,不能被解引用。


http://www.ppmy.cn/news/431816.html

相关文章

java从入门到起飞(二)——运算符

目录 前提——运算符概念算数运算符注意事项&#xff1a;字符的“”操作字符串的“”操作 赋值运算符注意事项&#xff1a; 自增自减运算符注意事项&#xff1a; 关系运算符注意事项&#xff1a; 逻辑运算符短路逻辑运算符注意事项&#xff1a; 三元运算符计算规则&#xff1a;…

【Flutter】Flutter 如何使用 flutter_swiper

文章目录 一、前言二、flutter_swiper 的概念三、Flutter 中的 flutter_swiper1. 使用的库2. 方法介绍 四、代码示例1. 简单示例2. 完整示例 五、总结 一、前言 在移动应用开发中&#xff0c;轮播图是一种常见的 UI 元素&#xff0c;它可以用来展示一系列的图片或者内容。在 F…

如何把小米和计算机共享,小米随身Wifi如何让手机共享电脑文件 小米wifi共享电脑文件教程...

小米近日推出了一款价格仅19.9元的小米随身Wifi&#xff0c;大小仅有硬币大小&#xff0c;便携性相当不错。小米随身Wifi与360随身Wifi功能上基本相同&#xff0c;只需将小米随身WiFi连接任何可以上网的电脑&#xff0c;就可以轻松为智能手机、Pad平板提供免费Wifi上网。另外小…

小米8se与电脑连接_小米8se非常划算,但很少有人买. 原因是什么?在

可以说&#xff0c;现在对于爱买手机的用户来说&#xff0c;手机的性价比非常重要&#xff0c;价格也更重要. 而现在为什么说国产手机比苹果好呢&#xff1f;这是因为现在很都买性价比高的手机&#xff0c;也就是说&#xff0c;便宜的手机. 虽然苹果的各种功能都比国产手机好&a…

小米5的android版本最好用,小米5有几个版本 小米5各版本区别对比

今天下午,小米在北京国家会议中心举行了春季新品发布会,正式发布了期待已久的新一代旗舰小米手机5。此次带来的小米5拥有多个版本,由于配置存在一些差异所以在售价方面也是有所不同的。那么究竟小米5有几个版本以及小米5各版本有哪些区别呢?下面电脑百事网小编为大家附上详…

2023认证杯数学建模第二阶段C题完整原创论文讲解

大家好呀&#xff0c;从昨天发布赛题一直到现在&#xff0c;总算完成了认证杯二阶段C题完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品论文。 C第二阶段完整论文共64页&#xff0c;一…

小米android怎么刷机教程,安卓刷机教程_小米刷机教程_手机刷机教程-IT资讯(PC6.com)...

可以使用pe启动&#xff0c;然后使用磁盘工具检查硬盘分区是否出错或被修改&#xff0c;试着修复一下硬盘的引导MBR。然后重启电脑&#xff0c;看问题是否还在。 2018-12-13 优酷世界杯在哪里看&#xff1f;世界杯优酷直播怎么看&#xff1f;优酷直播在哪里看&#xff1f;距离6…

入职小米的第三个月是怎样的一种体验?

您好&#xff0c;如果喜欢我的文章&#xff0c;可以关注我的公众号「量子前端」&#xff0c;将不定期关注推送前端好文~ 博主是位2021届已经毕业的落榜生&#xff0c;由于在大四没能很好的把握机会从而错失了自身进入大厂校招的招聘时间&#xff0c;也是在毕业后成功上岸百度腾…