C++STL---list知识汇总

前言

学习完list,我们会对STL中的迭代器有进一步的认识。list底层有很多经典的东西,尤其是他的迭代器。而list的结构是一个带头双向循环链表。

list没有reserve和resize,因为它底层不是连续的空间,它是用时随时申请,不用时随时释放。他也不支持随机访问,所以没有operator[ ]。而他有头插,尾插,头删,尾删,以及任意位置的插入删除。

严格来说,C++中list实际有两个:

第一个forward_list是单链表,它是C++11新增加的,它的使用场景很少。它不支持尾插,尾删,因为单链表尾插尾删的效率很低。并且它对任意位置做插入删除操作是在当前位置之后,因为当前位置之前得找前一个,也是一个O(n)的实现。唯一的优势也就是每个结点少一个指针。

第二个list是我们要学习的带头双向循环链表。

list的介绍

1.列表是序列容器,允许在序列中的任何位置进行恒定时间O(1)的插入和擦除操作,以及双向迭代。

2.列表容器底层是双链表;双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向前一个元素和后一个元素。

3.list与forward_list非常相似:主要区别在于forward_listobject是单个链表,因此它们只能向前迭代,以换取更小、更高效。

4.与其他基本标准序列容器(array,vector和deque)相比,list在任意位置插入,删除结点的效率更高。

5.list和forward_list最大的缺陷是不支持在任意位置的随机访问,其次,list还需要一些额外的空间,以保存每个结点之间的关联信息(对于存储的类型较小元素来说这可能是一个重要的因素)。

list的使用

1.list的构造
constructor接口说明
list()构造空的list对象
list(size_t n,const value_type& val =value_type())构造list对象中包含n个值为val的元素
list(const list& x)拷贝构造
lsit(Inputiterator first,Inputiterator last)用迭代区间构造list对象

这里我们只要记住用迭代区间构造list对象时,也可以用其他容器的迭代器就行,其他没什么好说的。

#include<iostream>
#include<vector>
#include<list>
using namespace std;
int main()
{vector<int> v = { 1, 2, 3, 4, 5 };list<int> lt1(v.begin(), v.end());//用vector的迭代器构造list对象	return 0;
}
2.list迭代器的使用
iterator接口说明
begin && end返回第一个元素的迭代器 && 返回最后一个元素下一个位置的迭代器
rbegin + rend返回第一个元素的 reserve_iterator,即 end 位置 && 返回最后一个元素下一个位置的 reverse_iterator,即 begin 位置

我们需要记住两点:

1.在遍历的时候vector和string我们可以使用小于或不等于做判断条件,但是list只能使用不等于。因为list内不是连续的空间。

2.因为list不支持operator[ ],所以我们遍历list的时候不能使用[ ]的方式遍历了

		vector<int> v = { 1, 2, 3, 4};list<int> lt(v.begin(), v.end());list<int>::iterator it1 = lt.begin();while(it1 != lt.end())//这里只能使用!=,不能使用<{cout << *it1 << " ";++it1;	}cout << endl
3.list容量大小的函数(empty && size)
empty判断list对象是否为空
size返回list中有效结点的个数
4.list结点接收的函数(front && back)
front返回list第一个结点的数据的引用
back返回list最后一个结点的数据的引用
5.list修改函数
push_back尾插一个值为val的结点
push_front头插一个值为val的结点
pop_back尾删一个结点
pop_front头删一个结点
insert在pos位置中插入值为val的结点
erase删除pos位置的结点
swap交换两个list对象中的元素
clear清空list对象中的有效元素

下面我们在一段代码中用例子来解释我们需要注意的地方:

#include<iostream>
#include<algorithm>
#include<vector>
#include<list>
#include<functional>
using namespace std;namespace std
{void test1(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.pop_front();lt.pop_front();lt.pop_front();lt.pop_front();//lt.pop_front();  //注意在头删、尾删时要保证list里还有数据,否则这里会报断言错误for (const auto& e : lt){cout << e << " ";}cout << endl;}void test2(){list<int> lt;lt.push_back(10);lt.push_back(20);lt.push_back(30);lt.push_back(40);list<int>::iterator pos = find(lt.begin(), lt.end(), 30);//list里也没有提供find函数,所以这里使用的是algorithm里的if (pos != lt.end()){lt.insert(pos, 3);}for (const auto& e : lt){cout << e << " ";}cout << endl;lt.clear();//clear不会把头节点清除,这里还可以继续插入数据lt.push_back(1);lt.push_back(2);for (const auto& e : lt){cout << e << " ";}cout << endl;}void test3(){list<int> lt1;lt1.push_back(1);lt1.push_back(2);list<int> lt2;lt2.push_back(2);lt2.push_back(1);list<int> lt3;lt3.push_back(1);lt3.push_back(2);lt3.push_back(3);lt3.push_back(4);//对于swap,建议使用容器里的,而不建议使用算法里的。它们效果一样,但是效率不一样lt1.swap(lt2);        //这是std::list::swap,专门为了list设计的,效率会更高//swap(lt1, lt2);     //这是std::swap,这是算法中的,通用的,底层发生的是深拷贝,效率低for (const auto& e : lt1){cout << e << " ";}cout << endl;for (const auto& e : lt2){cout << e << " ";}cout << endl;//所有的排序都满足,>是降序,<是升序,这里默认是升序//这个也是一个类模板,它是一个仿函数,所在头<functional>,后面我们会实现,sort所在头<algorithm>greater<int> g;lt3.sort(g);lt3.sort(greater<int>());//同上,可以直接写成匿名对象for (const auto& e : lt3){cout << e << " ";}cout << endl;//sort(lt3.begin(), it3.end());     //error//vector可以使用算法提供的sort()函数,但是list不行,因为本质sort()会用两个迭代器相减,而list的迭代器不支持减!!//unique的功能是去重,是algorithm提供的,去重的前提是排序,升序降序都行,如果不排序它只能去重相邻的数据lt3.unique();for (const auto& e : lt3){cout << e << " ";}cout << endl;//erase需要先find,而remove可以直接删除,有就全删,没有就不删,由algorithm提供lt3.remove(2);for (const auto& e : lt3){cout << e << " ";}cout << endl;//reverse的功能是逆置,对于带头双向循环链表的逆置比单链表简单,由algorithm提供lt3.reverse();for (const auto& e : lt3){cout << e << " ";}cout << endl;//merge的功能是合并//splice的功能是转移,它转移的是节点不是数据,很特殊的场景下才会使用到,我们以后在了解LRU可能还会再接触到}
}
int main()
{//std::test1();//std::test2();std::test3();return 0;
}

注意:

1.C++98不论什么容器都建议使用各自容器里的 swap,而不建议使用算法里的 swap。

因为无论什么容器使用算法中的swap()时都会涉及到深拷贝问题,并且需要深拷贝三次,代价极大。

而容器内的swap会根据各个容器的特性,进行交换。例如,两个list对象交换的时候,只需要交换两个对象的头指针指向就行;两个vector对象交换的时候,只需要交换两个vector对象中_start、_finish、_endofstorage三个指针的指向即可,不用做深拷贝,也就提高了效率。

2.关于迭代器的补充

从使用功能的角度分类:正向,反向,const,非const

从容器底层结构分类:单向,双向,随机

如单链表,哈希表迭代器就是单向,特点是能++,不能--;双向循环链表,map迭代器就是双向,特点是能++,也能--;string,vector,deque迭代器就是随机迭代器,特征是不仅能++,--,还能+,-,一般随机迭代器底层都是一个连续的空间。

这里我们可以通过算法里函数参数的命名来推断出他的含义:

我们看,例如sort函数内参数命名为RandomAccessIterator,也就是随机迭代器;而reverse函数内参数命名为BidirectionalIterator,也就是双向迭代器。

而我们使用的时候,要注意,比如:reserve函数的参数是双向迭代器,而string能不能使用呢?答案是可以的,因为string是随机迭代器,它满足双向迭代器的所有功能。但是例如:list能使用sort函数吗?答案是不能的,因为list迭代器是双向的,不满足随即迭代器的功能。也就是说功能多的是可以执行参数迭代器功能少的函数的,这实际上就是一种继承关系。

而我们这里就要明白一个道理:容器是用来存储数据的,而根据封装的要求,他的成员变量一般是私有的,而封装的本质就是通过合法的渠道去进行操作,那我可以提供成员函数来供使用者合法的操作,但是这样的话也不太好,因为底层结构差异大了以后,每种容器的操作起来的差别也会很大。例如,string,vector底层是数组,我们通过数组的方式进行操作,list底层是链表我们通过链表的方式进行操作,而其他复杂的数据结构,操作的方式会更不一样。所以迭代器的本质就是不破坏容器的封装性,不暴露容器底层实现细节的情况下,提供统一的方式去操作容器中存储的数据。只要我们会其中一种容器的迭代器,我们用其他容器的迭代器也没有问题。所以迭代器被称为”容器和算法之间的胶合剂“。

6.list迭代器失效问题

在具体介绍list迭代器之前,我们可以先将迭代器暂时理解为指针,迭代器失效就是迭代器所指向的结点无效,即该结点被删除了。list底层结构为带头双向循环链表,所以对list进行insert操作时不会导致list迭代器失效,只有在erase的时候才会失效,并且失效的只有被删除的结点,其他迭代器不会收到影响。

也就是说:

  • vector insert,pos会失效,因为它的物理空间是一个连续的数组,首先它可能会扩容,就会导致野指针问题;就算不扩容,挪动了数据,pos的意义也改变了,所以pos也失效了
  • vector erase,pos会失效,因为此时pos的意义已经改变了,为了解决这个问题,我们可以使用pos重新接收erase函数的返回值,其指向删除元素的下一个元素
  • list insert,pos不会失效,因为list底层是一个链表,每个结点都是独立的,insert后的数据属于新增的结点,而pos还是指向原来的位置
  • erase pos,pos会失效,因为pos指向的结点已经被释放了,为了解决这个问题,和vector erase的解决方法一样,我们也可以使用pos重新接收erase函数的返回值

以上就是本章的所有内容,谢谢大家!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/22813.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

windows 系统中部署 windows_exporter

从 github 中下载安装包到本地路径&#xff1a; Releases prometheus-community/windows_exporter (github.com) 后台运行方式windows_exporter&#xff0c;进入命令提示符执行以下命令&#xff1a; > powershell -Command "Start-Process D:\windows_exporter\wind…

快排与归并的算法(非递归版)

一.快排 1.递归法(方法多样) 1>hoare版 注&#xff1a;该方法小编已经在上篇博客中介绍过了&#xff0c;就不在这里过多赘述了&#xff0c;如果有兴趣的小伙伴可以看看小编的上篇博客哦 2>挖坑法 1&#xff09;方法介绍&#xff1a;定义最左边的数据为key&#xff0…

GLM-4本地部署的实战教程

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

Java面试题:Redis双写一致性问题

Redis双写一致性 缓存和数据库数据同步 正常流程: 读操作: 查询缓存,查询命中直接返回,没命中查询数据库将查询到的数据写入缓存,并设定超时时间 写操作: 删除缓存,修改数据库,在延时一段时间后再删除缓存 (延迟双删)延迟:等待数据库的主节点同步到从节点 因为如果先删…

QT: 读写ini配置文件(实现qml界面登录,修改)

目录 一.功能介绍 二.暴露属性 三.指定INI文件的路径和格式。 四.登录操作 1.检查INI文件中是否含有登录信息&#xff1b; 2.读取存储的ID&#xff1b; 3.读取存储的密码; 4.成功返回1&#xff1b;失败返回2&#xff1b; 五.修改账号 1.检查INI文件中是否含有登录信…

Java实现一个公共方法解析不同类型的表格

首先是公共方法 private String getCellValueAsString(Cell cell) {if (cell null) {return "";}String value "";switch (cell.getCellType()) {case STRING:value cell.getStringCellValue();break;case NUMERIC:if (DateUtil.isCellDateFormatted(ce…

实操:serverless-step-functions-local

目录 问题 依赖组件 调用流程 代码设置 1、安装依赖插件&#xff1a; 2、serverless.yml配置&#xff0c;我这里只提供stepfunction依赖的配置 3、业务代码中使用本地stepfunction 4、启动serverless offline 5、docker 安装启动stepfunction ⚠️注意 1、docker访问…

【Linux】进程(6):环境变量

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解Linux进程&#xff08;6&#xff09;&#xff1a;环境变量&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 &#xff08;A&#xff09;PATH方法1&#…

水经微图安卓版5.3.0发布

随时随地&#xff0c;微图一下&#xff01; 水经微图&#xff08;以下简称“微图”&#xff09;安卓版&#xff0c;新版已上线。 当前版本 当前版本号为&#xff1a;5.3.0-beta 如果你发现该版本中存在问题&#xff0c;请及时反馈给我们修订。 关于我们产品的版本控制&…

【面向就业的Liux基础】从入门到熟练,探索Linux的秘密(一)

主要帮助大家面向工作过程中Linux系统常用的命令联系&#xff0c;采用极致的实用主义&#xff0c;帮助大家节省时间。 文章目录 前言 一、linux系统 二、linux系统基本命令 1.Linux系统的目录结构 2. 常用命令介绍 3.命令演示 4.作业练习 总结 前言 主要帮助大家面向工作过程中…

微信小程序实现图生图(AI动漫特效)效果代码(触站API)

1.效果 触站AI图生图 2.本次用的是触站平台的API,我申请的适用积分,有水印(博主没钱)。如果需要没有水印的可以去买他们的资源包 3.首先我们需要去触站官网平台注册/登录账号(已注册可跳过该步骤) 4.开通API权限 我们可以在主页看到自己免费获取的500积分,用于接口调用…

微信小程序开发的详细解读

目录 小程序的ID 小程序的项目结构 小程序调试基础库 小程序调试 小程序配置文件 Pages配置 Windows配置 tabbar配置 页面配置 项目配置文件 sitemap文件配置 样式与组件 小程序常用组件 轮播图组件 图片组件 Text组件 跳转方式 滚动方式 字体图表使用 背景…

python入门3

文章目录 前言一、函数为什么要使用函数&#xff1f;函数定义函数定义和调用定义函数返回值定义空函数函数参数传递传递实参位置实参关键词实参默认值实参等效函数调用实参可选传递任意数量的实参任意数量关键字实参任意参数*与** 的区别使用元组和字典传参如果既有实参又有任意…

数据可视化---使用matplotlib绘制高级图表(2)

题目一&#xff1a;绘制人口金字塔图 编写程序。根据第8.6&#xff0c;绘制如下图的人口金字塔图。 运行代码&#xff1a; #绘制人口金字塔图 import numpy as np import pandas as pd import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] SimHei plt.rcParams[…

云服务器安装宝塔Linux面板全流程,新手教程!

云服务器如何宝塔Linux面板&#xff1f;阿小云以阿里云服务器为例安装宝塔Linux面板全流程&#xff0c;非常简单&#xff1a; 使用阿里云服务器安装宝塔面板教程&#xff0c;阿里云服务器网以CentOS操作系统为例&#xff0c;安装宝塔Linux面板&#xff0c;先远程连接到云服务器…

UML静态图-类图

概述 静态图包含类图、对象图和包图的主要目的是在系统详细设计阶段&#xff0c;帮助系统设计人员以一种可视化的方式来理解系统的内部结构和代码结构&#xff0c;包括类的细节、类的属性和操作、类的依赖关系和调用关系、类的包和包的依赖关系。 一、类图的表示法 类图(Cla…

轴承阀门5G智能工厂工业物联数字孪生平台,推进制造业数字化转型

轴承阀门5G智能工厂工业物联数字孪生平台作为其中的佼佼者&#xff0c;以其高效、智能、灵活的特点&#xff0c;为制造业的数字化转型提供了强有力的支撑。数字孪生技术&#xff0c;作为智能制造的重要一环&#xff0c;通过构建虚拟与现实相结合的数字化模型&#xff0c;实现了…

离散数学答疑 1

全功能连接词组&#xff1a; 最小全功能联结词组&#xff1a; C&#xff1a;祈使句 6->8是叫条件转化&#xff08;置换&#xff09;吗 反证法&#xff1a; 结论取反作为条件&#xff0c;并且在推理过程中&#xff0c;既推出有B也有反B&#xff0c;则推理成功 反证法算是间…

轻松追剧不费力:短剧小程序引领观剧新潮流

随着时代的进步和科技的发展&#xff0c;人们的娱乐方式也在不断变化。如今&#xff0c;短视频、短剧等碎片化内容正逐渐成为人们日常生活中的一部分。而短剧小程序&#xff0c;以其便捷、高效、内容丰富的特点&#xff0c;正在引领一场观剧新潮流。 一、短剧小程序的崛起 在快…

能获取淘宝商品简化链接的浏览器书签

零&#xff0e;冗长的商品链接 访问网页版本淘宝时&#xff0c;浏览器的地址栏显示的链接太长就像这样(此链接非真实商品)&#xff1a; 于是使用如下方法 一&#xff0e;使用浏览器书签获取淘宝商品简化链接 1.新建书签 Chrome - 打开书签管理器(CtrlShiftO) - 左侧选择书签…