C++STL详解(五)——list类的接口详解

一.list的介绍

list容器的底层是双向循环带头链表,在CPP中,我们对双向循环带头链表进行了一定程度的封装。

如果你不了解双向链表,那么可以浏览此片博文:双向链表

二.list的定义方式以及赋值

2.1list的构造方式

在这里我们要学习六种list的构造方式。

构造1:构造一个某类型容器

list<int> list1;

构造2:构造一个含有一个值的某类型容器

list<int> list2(3);//构造只含有一个数值3的结点的list容器

构造3:构造一个含有n个值的某类型容器

list<int> list3(3, 5);//构造含有三个数值都是5的结点的list容器

构造4:复制某个容器构造该容器的复制品

list<int> list4(list3);//拷贝构造

构造5:使用某个迭代器区间进行构造

list<int> list5(list3.begin(), list3.end());//迭代器区间初始化

 构造6:使用列表构造一个某类型容器

	list<int> list6{ 1,2,3,4,5,6 };//使用列表构造

 2.2operator=重载函数

同样的,我们也可以使用重载后的operator=操作符来进行给一个空的list容器赋值。

方式1:将别的同类型容器赋值给该容器

list<int> list7 = list5;

方式2:使用列表

list<int> list8 = { 1,2,3,4,5,6 };

三.list的插入和删除

list库中提供给我们大家的需要我们学习的函数是头插和尾插函数以及头删和尾删函数。

下面我们逐个进行学习。

3.1push_front和pop_front

我们可以通过这两个函数进行头插和头删。

函数原型如下:

	void push_front(const value_type& val);void pop_front();

 我们可以使用这两个函数对list的头部进行删除和插入操作。

如下:

void test2()
{list<int> list1;list1.push_front(3);list1.push_front(4);list1.push_front(5);list1.push_front(6);print(list1);//为了方便写博客,这个是自己写的函数。list1.pop_front();print(list1);
}

结果: 

6 5 4 3
5 4 3

3.2push_back和pop_back

 我们可以通过这两个函数进行尾插和尾删。

函数原型如下:

	void push_back(const value_type& val);void pop_back();

 我们可以使用这两个函数对list的尾部进行删除和插入操作。

void test2()
{list<int> list2;list2.push_back(1);list2.push_back(2);list2.push_back(3);list2.push_back(4);print(list2);list2.pop_back();print(list2);
}

 结果:

1 2 3 4
1 2 3

3.3insert

insert函数用于在指定位置插入数据。 

insert函数的函数原型如下:

iterator insert(iterator position, const value_type& val);//在pos位置插入一个val
void insert(iterator position, size_type n, const value_type& val);//在position位置插入n个val
void insert(iterator position, InputIterator first, InputIterator last);//在position位置插入[first,last)区间的内容。

insert:

  • 函数1:在position位置插入一个val
  • 函数2:在position位置插入n个val
  • 函数3:在position位置插入迭代器区间[first,last)中的内容。
void test3()
{list<int> l1(3, 1);l1.insert(l1.begin(), 2);print(l1);l1.insert(l1.begin(), 3,3);print(l1);list<int> l2{ 3,2,1 };l1.insert(l1.begin(), l2.begin(),l2.end());print(l1);
}

结果: 

2 1 1 1
3 3 3 2 1 1 1
3 2 1 3 3 3 2 1 1 1

3.4erase

 erase函数用于在指定位置删除数据。 

erase函数的函数原型如下:

iterator erase(iterator position);
iterator erase(iterator first, iterator last);

erase:

  • 函数1:删除position位置的数据。
  • 函数2:删除迭代区间[first,last)中的数据。 
void test3()
{list<int> l1(3, 1);l1.insert(l1.begin(), 2);l1.insert(l1.begin(), 3,3);list<int> l2{ 3,2,1 };l1.insert(l1.begin(), l2.begin(),l2.end());l1.erase(l1.begin());print(l1);l1.erase(++l1.begin(), --l1.end());//注意是前置++和--,而不是后置的++--print(l1);
}

结果:

2 1 3 3 3 2 1 1 1
2 1

四.list的迭代器使用

list和别的容器不同的地方在于:list的迭代器只支持++和--,不支持+和-。

因此,下面的操作是不合法的:

	list<int>::iterator it = find(3, l1.begin(), l1.end());it + 1;

4.1begin和end

begin()end()是list的正向迭代器函数

  • begin()函数返回list容器的第一个位置
  • end()函数返回list容器的最后一个位置的下一个位置

图解如下:

4.2rbegin和rend

rbegin()rend()是list的反向迭代器函数 

  • rbegin()函数返回vector容器的最后一个位置
  • rend()函数返回vector容器的第一个位置的前一个位置

图解如下: 

同样的,我们可以使用以上的两种迭代器对list内的元素进行遍历。

如下:

void test4()
{list<int> l1{ 1,2,3,4 };//正向迭代器list<int>::iterator it1 = l1.begin();while (it1 != l1.end()){cout << *it1 << ' ';it1++;}cout << endl;//反向迭代器list<int>::reverse_iterator it2 = l1.rbegin();while (it2 != l1.rend()){cout << *it2 << ' ';it2++;}cout << endl;
}

结果: 

1 2 3 4
4 3 2 1

五.list的元素获取

5.1front和back

front用于获取头部元素,back用于获取尾部元素。

这两个函数比较简单,我们使用一下即可。

void test5()
{list<int> l1{ 1,2,3,4 };cout << l1.front() << endl;cout << l1.back() << endl;
}

结果:

1

4

六.list的空间控制

6.1size

我们在初始化一个list容器之后,可以通过这个函数查看该容器的有效数据个数。

函数原型如下:

void size();

在我们用两个1初始化一个list容器之后,可以使用这个函数查看它的有效数据个数。 

6.2resize

使用这个函数可以重设函数的有效数据。

函数原型如下:

void resize(size_type n, value_type val = value_type());

resize:

  • 如果有效数据个数大于当前size,若规定了特定值则用特定值填充,否则用数据类型的默认值填充
  • 如果有效数据个数小于当前size,则只保留前size个数据。
void test6()
{list<int> l1(2, 1);cout << l1.size() << endl;l1.resize(5);print(l1);//1 1 0 0 0 l1.resize(10, 1); print(l1);//1 1 0 0 0 1 1 1 1 1l1.resize(3);print(l1);//1 1 0
}

6.3empty

empty函数判断list容器是否为空的函数。

empty:

  • 如果vector容器为空,则返回真
  • 如果vector容器不为空,则返回假。
	list<int> l1(2, 1);list<int> l2;cout << l1.empty() << endl;//0-->假cout << l2.empty() << endl;//1-->真

6.4clear

clear函数用于清除list容器中的数据并置size为0.

clear:

  • 清除掉list容器内所有的元素,并将size置为0
list<int> l1(2, 1);
cout << l1.empty() << endl;//0-->假
l1.clear();
cout << l1.empty() << endl;//1-->真

6.5assign

assign函数用于将新内容分配给容器,替换其当前内容。

assign函数原型如下:

	void assign(InputIterator first, InputIterator last);void assign(size_type n, const value_type & val);

assign: 

  • 函数2:将所给迭代器区间当中的内容分配给容器。
  • 函数1:将n个值为val的数据分配给容器。
void test13()
{list<int> l1(3, 1);list<int> l2(3,2);print(l1);//1 1 1l1.assign(5, 2);print(l1);///2 2 2 2 2l1.assign(l2.begin(), l2.end());print(l1);//2 2 2
}

结果:

1 1 1
2 2 2 2 2
2 2 2

 

七.list的操作相关函数

7.1sort

sort函数是用于升序排列list内的元素的。

函数原型如下:

void sort();

sort:

  • 将list内的元素默认排为升序。 
void test7()
{list<int> l1{ 10,9,8,7,6,5,4,3,2,1 };print(l1);//10 9 8 7 6 5 4 3 2 1l1.sort();print(l1);//1 2 3 4 5 6 7 8 9 10
}

结果:

10 9 8 7 6 5 4 3 2 1
1 2 3 4 5 6 7 8 9 10

7.2splice

splice的功能类似于vector的insert函数,它可以用于在指定位置将另一个list容器插入。

但是,这个和insert函数不同的是,splice函数本质是一种转移,而不是复制。

splice的函数原型如下:

void splice(iterator position, list& x);
void splice(iterator position, list& x, iterator i);
void splice(iterator position, list& x, iterator first, iterator last);

splice:

  • 函数1:在position位置转移x容器的所有元素
  • 函数2:在position位置转移xi位置的元素
  • 函数3:   在position位置转移x的[first,last)区间内的元素。
void test8()
{list<int> l1{ 5,4,3,2,1 };list<int> l2{ 6,7,8,9,10 };list<int> l3{ 0,11,12,13 };list<int> l4{ 0,11,12,13 };l1.splice(l1.end(), l2);print(l1);//5 4 3 2 1 6 7 8 9 10l1.splice(l1.end(), l3,++l3.begin());print(l1);//5 4 3 2 1 6 7 8 9 10 11 print(l3);//0 12 13l3.splice(l3.begin(), l4,++l4.begin(),--l4.end());print(l3);//11 12 0 11 12print(l4);//0 13
}

 结果:

5 4 3 2 1 6 7 8 9 10
5 4 3 2 1 6 7 8 9 10 11
0 12 13
11 12 0 12 13
0 13
 

这里需要大家注意的是,splice函数的本质是转移!

现在我通过画图的形式来解析这段代码:

四个容器:

第一次转移:

第二次转移:

第三次转移:

7.3remove

remove函数是用来删除list容器内的值的。

函数原型如下:

void remove (const value_type& val);

remove:

  • remove函数可以删除掉容器内所有值是val的结点 
void test9()
{list<int> l1{ 5,4,4,3,2,1 };print(l1);//5 4 4 3 2 1l1.remove(4);print(l1);//5 3 2 1
}

 结果:

5 4 4 3 2 1
5 3 2 1

7.4unique

这个函数是用来去除相同的元素的。

它会拿当前的元素和下一个元素进行比较,如果和下一个元素相同的话,则删掉下一个元素。

因为它的这个特性,我们常用于有序列表的去重。

函数原型如下:

void unique();

我们可以通过下面这段代码体会到这个函数的性质:相邻比较,相等删除。 

void test10()
{list<int> l1{ 1,1,3,3,4,1 };l1.unique();print(l1);//1 3 4 1
}

结果:

1 3 4 1 

7.5merge

merge函数用于合并两个有序list容器。

merge函数的原型如下:

 void merge (list& x);

merge:

  • merge函数用于合并两个有序的list容器,合并后的list容器依旧有序。
  • 作为参数被传递的容器被合并后会为空
void test11()
{list<int> l1{1,3,5,7,9};list<int> l2{2,4,6,8,10};l1.merge(l2);print(l1);//1 2 3 4 5 6 7 8 9 10print(l2);//
}

结果:

1 2 3 4 5 6 7 8 9 10

 7.6reverse

reverse用于翻转链表。

函数原型如下: 

void reverse();
void test12()
{list<int> l1{ 1,3,5,7,9 };l1.reverse();print(l1);
}

结果:

9 7 5 3 1 

7.7swap

swap函数用于交换两个容器的内容。

void test14()
{list<int> l1(3, 1);list<int> l2(3, 2);l1.swap(l2);print(l1);//2 2 2print(l2);//1 1 1
}

结果:

2 2 2
1 1 1 

八.list的迭代器失效问题

在之前的文章中提过这个问题,此处大家可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,而双向循环链表的扩容和顺序表不同,是不会出现异地扩容的现象的。因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

下面的这段代码就是非常典型的一段迭代器失效的代码: 

void test15()
{int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array + sizeof(array) / sizeof(array[0]));auto it = l.begin();while (it != l.end()){// erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值l.erase(it);++it;}
}

和vector一样,我们在使用前给迭代器赋值即可。

void test15()
{int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array + sizeof(array) / sizeof(array[0]));list<int>::iterator it = l.begin();while (it != l.end()){it=l.erase(it);++it;//这行代码和上面两行代码等效//l.erase(it++)}
}

九.后记

有关list的模拟实现可参考博主的下篇博文。

如果你想更深入的了解list的相关内容,可参考cpp官网:cpp官网

码字不易,给个点赞收藏叭~~~

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

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

相关文章

Haproxy 下载、编译部署、使用

文章目录 前言Haproxy 下载、编译部署、使用1. 下载2. 编译部署3. 使用3.1. 验证配置文件3.2. 启动 HAProxy 并指定配置文件路径3.3. 关闭HAProxy3.4. 重载HAProxy 3. 测试 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&am…

PCB工艺边设计准则

在PCB设计时&#xff0c;通常会在电路板的边缘预留一定的空间&#xff0c;这部分空间被称为工艺边。它有助于在生产过程中确保电路板的尺寸和形状的准确性。以使得组装时更加顺畅、便捷。而工艺边的加工&#xff0c;使得线路板上的元件可以精准地与设备对接&#xff0c;从而提高…

pythonGame-实现简单的贪食蛇游戏

通过python简单复现贪食蛇游戏。 使用到的库函数&#xff1a; import pygame import time import random 游戏源码&#xff1a; import pygame import time import randompygame.init()white (255, 255, 255) yellow (255, 255, 102) black (0, 0, 0) red (213, 50, 80…

鸿蒙(HarmonyOS)下拉选择控件

一、操作环境 操作系统: Windows 11 专业版、IDE:DevEco Studio 3.1.1 Release、SDK:HarmonyOS 3.1.0&#xff08;API 9&#xff09; 二、效果图 三、代码 SelectPVComponent.ets Component export default struct SelectPVComponent {Link selection: SelectOption[]priva…

【C++】选择结构案例-三只小猪称体重

案例问题 假设有三只小猪A、B、C&#xff0c;在输入三者体重后希望能输出他们各自的体重并测出谁最重 思路 先让A与B相比较&#xff0c;如果A重&#xff0c;则让A和C相比较&#xff0c;如果A重则输出A最重&#xff0c;否则输出C最重 在最开始的条件&#xff08;AB相比较&am…

Jetpack Compose 通过 OkHttp 发送 HTTP 请求的示例

下面是一个使用 Kotlin 和 Jetpack Compose 来演示通过 OkHttp 发送 HTTP 请求的示例。这个示例包括在 Jetpack Compose 中发送一个 GET 请求和一个 POST 请求&#xff0c;并显示结果。 添加okhttp依赖 首先&#xff0c;在你的 build.gradle.kts 文件中添加必要的依赖&#xf…

【Python系列】isin用法

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

通过强大的语义层增强现代数据湖

在现代数据湖架构中&#xff0c;语义层通过向数据添加有意义的上下文来发挥至关重要的作用&#xff0c;否则这些上下文会丢失。此层充当现代数据湖&#xff08;数据仓库和查询引擎&#xff09;处理层中未整理的原始数据与利用此数据的工具和应用程序之间的桥梁。此服务对 AI 特…

微信小程序-自定义tabBar

通过官网给出的示例自己实现了自定义的tabBar&#xff0c;但结果发现 无法监听页面生命周期函数 结语&#xff1a;原想的是实现不一样的效果&#xff08;如下&#xff09; 故尝试了自定义tabBar&#xff0c;虽然做出来了&#xff0c;但也发现这个做法存在不足&#xff1a; 在…

电子签章-开放签应用

开放签电子签章系统开源工具版旨在将电子签章、电子合同系统开发中的前后端核心技术开源开放&#xff0c;适合有技术能力的个人 / 团队学习或自建电子签章 \ 电子合同功能或应用&#xff0c;避免研发同仁在工作过程中重复造轮子&#xff0c;降低电子签章技术研发要求&#xff0…

Maven 的模块化开发示例

Maven 的模块化开发是一种非常有效的软件开发方式&#xff0c;它允许你将一个大型的项目分割成多个更小、更易于管理的模块&#xff08;modules&#xff09;。每个模块都可以独立地构建、测试和运行&#xff0c;这不仅提高了开发效率&#xff0c;也便于团队协作和项目的维护。以…

优化医疗数据管理:Kettle ETL 数据采集方案详解

在现代医疗保健领域&#xff0c;数据的准确性、完整性和及时性对于提高医疗服务质量和患者护理至关重要。为了有效管理和利用医疗数据&#xff0c;Kettle ETL&#xff08;Extract, Transform, Load&#xff09;数据采集方案成为了许多医疗机构的首选工具之一。本文将深入探讨Ke…

C++ 列式内存布局数据存储格式 Arrow

Apache Arrow 优点 : 高性能数据处理&#xff1a; Arrow 使用列式内存布局&#xff0c;这特别适合于数据分析和查询操作&#xff0c;因为它允许对数据进行高效批量处理&#xff0c;减少CPU缓存未命中&#xff0c;从而提升处理速度。 零拷贝数据共享&#xff1a; Arrow …

SPSS个人版是什么软件

SPSS是一款数据统计、分析软件&#xff0c;它由IBM公司出品&#xff0c;这款软件平台提供了文本分析、大量的机器学习算法、数据分析模型、高级统计分析功能等&#xff0c;软件易学且功能非常强大&#xff0c;可以使用SPSS制作图表&#xff0c;例如柱状、饼状、折线等图表&…

Pytorch使用教学7-张量的广播

PyTorch中的张量具有和NumPy相同的广播特性&#xff0c;允许不同形状的张量之间进行计算。 广播的实质特性&#xff0c;其实是低维向量映射到高维之后&#xff0c;相同位置再进行相加。我们重点要学会的就是低维向量如何向高维向量进行映射。 相同形状的张量计算 虽然我们觉…

SpringBoot 实现图形验证码

一、最终结果展示 二、前端代码 2.1 index.html <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8"><title>验证码</title><style>#inputCaptcha {height: 30px;vertical-align: middle;}#verifica…

C95之重要特性及用法实例(五十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列…

JavaWeb学习——请求响应、分层解耦

目录 一、请求响应学习 1、请求 简单参数 实体参数 数组集合参数 日期参数 Json参数 路径参数 总结 2、响应 ResponseBody&统一响应结果 二、分层解耦 1、三层架构 三层架构含义 架构划分 2、分层解耦 引入概念 容器认识 3、IOC&DI入门 4、IOC详解 …

实时同步:使用 Canal 和 Kafka 解决 MySQL 与缓存的数据一致性问题

目录 1. 准备工作 2. 将需要缓存的数据存储 Redis 3. 监听 canal 存储在 Kafka Topic 中数据 1. 准备工作 1. 开启并配置MySQL的 BinLog&#xff08;MySQL 8.0 默认开启&#xff09; 修改配置&#xff1a;C:\ProgramData\MySQL\MySQL Server 8.0\my.ini log-bin"HELO…

本田Honda EDI项目案例:非EDI标准的数据格式转换与传输

近期知行帮助东风本田Honda的供应商E公司成功实施EDI项目&#xff0c;与以往采用X12、EDIFACT等EDI标准的项目不同&#xff0c;Honda向其供应商提供API接口&#xff0c;以JSON的格式传输库存信息以及生产计划。 EDI需求概览 Honda提供公开的WSRM系统供应商API接口&#xff0c…