C++初阶:STL详解(六)——list的介绍和使用

✨✨小新课堂开课了,欢迎欢迎~✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C++:由浅入深篇

小新的主页:编程版小新-CSDN博客

前言:

前面我们已经了解了string,vector的用法以及他们的底层实现,今天我们再来学习了一个新的容器list,这几个容器之间有很大的相通性,我们来一起看一下吧。

一.什么是list ?

std::list是C++模板库(STL)中的一个双向链表容器。它提供了一种灵活的方式来储存和操作一组元素。

和vector,string一样,list也是一个模板类,使用时只需指定元素类型,编译器会确保类型安全。它定义在头文件(include<list>)中,可以使用list来存储和访问任意类型的对象。 

二.list的特性

1.双向链表——每个节点包含指向前一个和后一个的指针,因此它可以在进行高效的插入删除操作

2.动态大小——根据需要动态增加和减少元素个数,没有空间浪费

3.非连续储存——元素在内存中是不连续的,不支持下标的随机访问

4.迭代器支持——支持STL迭代器,可以使用标准的算法进行遍历和操作。在进行插入和删除操作时,除了被指向被删除元素的迭代器失效,其他迭代器通常不失效,这点与vector也不同。vector在进行插入操作时迭代器不一定失效,在进行删除操作时被删除元素及其后面元素的迭代器都失效。

C++初阶:STL详解(四)——vector迭代器失效问题-CSDN博客(可供参考)

三.list的文档介绍

list - C++ Reference(英文版)

https://zh.cppreference.com/w/cpp/container/list(中文版)

 

四.list的使用

list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理。在实际中我们熟悉常见的接口就可以,下面列出了哪些接口是要重点掌握的 。我们在后面模拟实现的时候,也是实现这些常见的接口。

4.1list的构造

构造函数( (constructor))接口说明

list (size_type n, const value_type& val =value_type())

构造的list中包含n个值为val的元素

list()构造空的list

list (const list& x)拷贝构造函数

list (InputIterator first, InputIterator last)用[first, last)区间中的元素构造list

1.list() :构造一个空的list

list<int> st1;//构造一个空list

2.list (size_type n, const value_type& val =value_type()) :构造一个list,元素个数为n,元素为val 

list<int> st2(10, 1);//用n个值为val的元素构造一个list

3.list (const list& x) :拷贝构造

list<int> st3(st2);//拷贝构造

4.list (InputIterator first, InputIterator last) : 使用迭代器区间进行初始化构造

list<int> st4(st2.begin(), st2.end());//使用一个迭代器区间构造list

补充:构造数组某段区间的复制品

int myints[] = { 16,2,77,29 };
list<int> st5(myints, myints + sizeof(myints) / sizeof(int));//构造数组某段区间的复制品

4.2list的迭代器

此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点。

函数声明接口说明

begin +end返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器

rbegin+ rend

返回指向列表最后一个元素的反向迭代器+返回指向列表第一个元素之前的反向迭代器

begin+end

//正向迭代器遍历
void list1()
{list<int> st(10, 1);list<int>::iterator it = st.begin();while (it != st.end()){cout << *it << " ";it++;}cout << endl;
}

rbegin+rend

//反向迭代器遍历
void list2()
{list<int> st(10, 1);list<int>::reverse_iterator rit = st.rbegin();while (rit != st.rend()){cout << *rit << " ";rit++;}cout << endl;
}

注意:

1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动

2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

4.3list的容量

函数声明接口说明

empty

检测list是否为空,是返回true,否则返回false

size返回list中有效节点的个数

size和empty

void list3()
{list<int>st(10, 1);cout << st.size() << endl;//list的有效节点个数cout << st.empty() << endl;//不为空返回0,为空返回非0
}

4.4list的访问

函数声明接口说明

front返回list的第一个节点中值的引用

back返回list的最后一个节点中值的引用

front和back

void list4()
{list<int> st;st.push_back(1);st.push_back(2);st.push_back(3);st.push_back(4);st.push_back(5);cout << st.front() << endl;//返回第一个节点中值的引用 1cout << st.back() << endl;//返回最后一个节点值的引用 5
}

 4.5list的增删查改

push_front和pop_front

push_front用于头插一个元素,pop_front用于头删一个元素

void list5()
{list<int> st;st.push_back(1);st.push_back(2);st.push_back(3);st.push_back(4);st.push_front(5);//在首元素前插入cout << "插入前:";for (auto ch : st){cout << ch << " ";//5 1 2 3 4}cout << endl;st.pop_front();//删除首元素cout << "插入后:";for (auto ch : st){cout << ch << " ";// 1 2 3 4}
}

push_back和pop_back

push_back用于尾插一个元素,pop_back用于尾删一个元素。

void list6()
{list<int> st;st.push_back(1);st.push_back(2);st.push_back(3);st.push_back(4);st.push_back(5);//在尾部插入for (auto ch : st){cout << ch << " ";// 1 2 3 4 5}cout << endl;st.pop_back();//删除尾部元素cout << "插入后:";for (auto ch : st){cout << ch << " ";// 1 2 3 4}
}

insert

list当中的insert函数支持三种插入方式:

  1. 在指定迭代器位置插入一个数。
  2. 在指定迭代器位置插入n个值为val的数。
  3. 在指定迭代器位置插入一段迭代器区间(左闭右开)。
void list7()
{list<int> st;st.push_back(1);st.push_back(2);st.push_back(3);st.push_back(4);//在指定迭代器位置插入一个数st.insert(st.begin(), 0); //在容器开头插入0//在指定迭代器位置插入n个valst.insert(st.begin(), 5, -1); //在容器开头插入5个-1//在指定迭代器位置插入一段迭代器区间int myarray[] = { 501,502,503 };st.insert(st.begin(), myarray, myarray + 3);//在容器开头插入 501 502 503for (auto ch : st){cout << ch << " ";// 501 502 503 -1 -1 -1 -1 -1 0 1 2 3 4}}

 erase

list当中的erase函数支持两种删除方式:

  1. 删除指定迭代器位置的元素。
  2. 删除指定迭代器区间(左闭右开)的所有元素。
void list8()
{list<int> st;st.push_back(1);st.push_back(2);st.push_back(3);st.push_back(4);st.push_back(5);st.push_back(6);//删除指定位置迭代器的元素st.erase(st.begin()); //删除容器中的第一个元素//删除指定迭代器区间(左闭右开)的所有元素。st.erase(st.begin(), st.end()); //删除在该迭代器区间内的元素(左闭右开)for (auto ch : st){cout << ch << " ";}}

resize

resize的两种情况:

  1. 当所给值大于当前的size时,将size扩大到该值,扩大的数据为第二个所给值,若未给出,则默认为容器所存储类型的默认构造函数所构造出来的值。
  2. 当所给值小于当前的size时,将size缩小到该值。
void list9()
{list<int> st;st.push_back(1);st.push_back(2);st.push_back(3);st.push_back(4);st.push_back(5);st.push_back(6);st.resize(10, 9);//将size扩大到10,以9来填充for (auto ch : st){cout << ch << " ";//1 2 3 4 5 6 9 9 9 9 }cout << endl;st.resize(4);//将size缩小到4for (auto ch : st){cout << ch << " "; //1 2 3 4}cout << endl;}

assign

list中的assign函数用于将新内容分配给容器,替换其当前内容,新内容的赋予方式有两种:

  1. 将n个值为val的数据分配给容器。
  2. 将所给迭代器区间当中的内容分配给容器。
void list10()
{list<int> st;st.push_back(1);st.push_back(2);st.push_back(3);st.push_back(4);st.push_back(5);st.push_back(6);for (auto ch : st){cout << ch << " ";//1 2 3 4 5 6}cout << endl;//将新内容分配给容器,替换当前的内容st.assign(10, 1);//用n个val替换for (auto ch : st){cout << ch << " "; //1 1 1 1 1 1 1 1 1 1}cout << endl;//将新内容分配给容器,替换当前的内容int myints[] = { 1776,7,4 };st.assign(myints, myints + 3);//迭代器区间所指的内容进行替换for (auto ch : st){cout << ch << " ";//1776 7 4}cout << endl;}

swap

交换两个list的元素。

void list11()
{list<int> st1;st1.push_back(1);st1.push_back(2);st1.push_back(3);st1.push_back(4);st1.push_back(5);st1.push_back(6);cout << "st1交换前:";for (auto ch : st1){cout << ch << " ";//1 2 3 4 5 6}cout << endl;list<int> st2;st2.push_back(1);st2.push_back(2);st2.push_back(3);st2.push_back(4);cout << "st2交换前:";for (auto ch : st2){cout << ch << " ";//1 2 3 4}cout << endl;st1.swap(st2);cout << "st1交换后:";for (auto ch : st1){cout << ch << " ";//1 2 3 4 }cout << endl;cout << "st2交换前:";for (auto ch : st2){cout << ch << " ";//1 2 3 4 5 6}cout << endl;
}

clear

清空容器的内容。

void list12()
{list<int> st;st.push_back(1);st.push_back(2);st.push_back(3);st.push_back(4);for (auto ch : st){cout << ch << " ";//1 2 3 4 }cout << endl;cout << st.size() << endl;//4st.clear();//清空容器的内容cout << st.size() << endl;//0}

4.6list的操作函数

sort

给容器中的内容排序,默认是排升序。

void list01()
{list<int> st;st.push_back(1);st.push_back(2);st.push_back(9);st.push_back(3);st.push_back(5);st.push_back(4);st.push_back(10);st.push_back(6);st.sort();//默认排升序for (auto ch : st){cout << ch << " ";//1 2 3 4 5 6 9 10}cout << endl;}

splice

用于两个list的拼接,其有三种拼接方式:

  1. 将整个容器拼接到另一个容器的指定迭代器位置。
  2. 将容器当中的某一个数据拼接到另一个容器的指定迭代器位置。
  3. 将容器指定迭代器区间的数据拼接到另一个容器的指定迭代器位置。
void list02()
{list<int> st1(4, 1);list<int> st2(5, 2);st1.splice(st1.begin(), st2); //将容器st2拼接到容器st1的开头for (auto ch : st1){cout << ch << " "; //2 2 2 2 2 1 1 1 1}cout << endl;list<int> st3(4, 1);list<int> st4(5, 2);st3.splice(st3.begin(), st4, st4.begin()); //将容器st4的第一个数据拼接到容器st4的开头for (auto ch : st3){cout << ch << " ";//2 1 1 1 1}cout << endl;list<int> st5(4, 1);list<int> st6(5,2);st5.splice(st5.begin(), st6, st6.begin(), st6.end()); //将容器st6的指定迭代器区间内的数据拼接到容器st6的开头for (auto ch : st5){cout << ch << " ";//2 2 2 2 2 1 1 1 1}cout << endl; }

remove

删除容器中特定元素。

void list03()
{list<int> st;st.push_back(1);st.push_back(2);st.push_back(3);st.push_back(4);st.remove(3);//删除指定元素for (auto ch : st){cout << ch << " ";//1 2 4}cout << endl;}

remove_if

删除容器中满足条件元素。

bool single_digit(const int& val)
{return val < 10;
}void list04()
{list<int> st;st.push_back(1);st.push_back(2);st.push_back(3);st.push_back(4);st.push_back(10);st.push_back(20);st.push_back(30);st.push_back(40);st.remove_if(single_digit);//删除满足条件的元素for (auto ch : st){cout << ch << " ";//10 20 30 40}cout << endl;}

unique

删除容器中连续重复元素。

void list05()
{list<int> st;st.push_back(1);st.push_back(2);st.push_back(2);st.push_back(4);st.push_back(10);st.push_back(10);st.push_back(10);st.push_back(40);//有序//如果list无序,可以使用sort使其有序st.unique();//删除连续重复元素for (auto ch : st){cout << ch << " ";//1 2 4 10 40}cout << endl;}

merge

合并两个有序容器,合并后依然有序。

void list06()
{list<int> st1;st1.push_back(8);st1.push_back(3);st1.push_back(1);st1.push_back(6);list<int> st2;st2.push_back(0);st2.push_back(10);st2.push_back(2);st2.push_back(5);st1.sort();st2.sort();//先满足是两个有序链表//合并两个有序链表st1.merge(st2);//合并后依然有序for (auto ch : st1){cout << ch << " ";//0 1 2 3 5 6 8 10}cout << endl;
}

reverse 

将容器中的元素位置进行逆置。

void list07()
{list<int> st1;st1.push_back(8);st1.push_back(3);st1.push_back(1);st1.push_back(6);cout << "逆置前:";for (auto ch : st1){cout << ch << " ";//8 3 1 6}cout << endl;st1.reverse();cout << "逆置后:";for (auto ch : st1){cout << ch << " ";//6 1 3 8}cout << endl;
}

五.list的迭代器失效

前面说过,此处大家可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。vector的迭代器失效理解了,这里就没什么可以说的了。

C++初阶:STL详解(四)——vector迭代器失效问题-CSDN博客

void TestListIterator1()
{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;}
} 

 运行结果:

改正:万能钥匙:在使用前,对迭代器重新赋值即可。

void TestListIterator()
{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()){l.erase(it++); // it = l.erase(it);}
}

 六.list 和vector的对比

vectorlist
底 层 结 构动态顺序表,一段连续空间带头结点的双向循环链表
随 机 访 问支持随机访问,访问某个元素效率O(1)不支持随机访问,访问某个元素O(N)
插 入 和 删 除任意位置插入和删除效率低,需要搬移元素,时间复杂度为O(N),插入时有可能需要增容,增容:开辟新空间,拷贝元素,释放旧空间导致效率更低任意位置插入和删除效率高,
不需要搬移元素,时间复杂度
为O(1)
空间利用率底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高底层节点动态开辟,小节点容
易造成内存碎片,空间利用率
低,缓存利用率低
迭 代 器 失 效在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响
使 用 场 景需要高效存储,支持随机访问,不关心插入删除效率大量插入和删除操作,不关心
随机访问
迭代器原生态指针对原生态指针(节点指针)进行
封装

总结:

list的常见接口及其用法,我们已经介绍完了,准备好一起来模拟实现list了嘛,我们下篇见。 

感谢各位大佬的观看,创作不易,还请各位大佬支持~ 

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

c++----io流

提示&#xff1a;以下 是本篇文章正文内容&#xff0c;下面案例可供参考 1.标准io流 (1)数据的循环输入 对于内置类型&#xff1a;cin和cout直接使用&#xff0c;c已经重载了 (2)对于自定义类型&#xff1a; 需要我们自己对类型进行重载 2.文件io流 ifstream ifile(只输入…

机器学习中结构风险最小化的正则化项用途及原理详解

一、概述 数学和工程领域&#xff0c;正则(Regularize)意味着使某物标准化或规范化&#xff0c;在机器学习领域指的是使模型的行为更加规范化&#xff0c;以避免极端或过于复杂的模型。 正则化项&#xff08;Regularization Term&#xff09;是机器学习模型中用于控制模型复杂…

力扣72-编辑距离(Java详细题解)

题目链接&#xff1a;力扣72-编辑距离 前情提要&#xff1a; 因为本人最近都来刷dp类的题目所以该题就默认用dp方法来做。 dp五部曲。 1.确定dp数组和i下标的含义。 2.确定递推公式。 3.dp初始化。 4.确定dp的遍历顺序。 5.如果没有ac打印dp数组 利于debug。 每一个dp…

鸿蒙OpenHarmony【轻量系统内核扩展组件(动态加载)】子系统开发

基本概念 在硬件资源有限的小设备中&#xff0c;需要通过算法的动态部署能力来解决无法同时部署多种算法的问题。以开发者易用为主要考虑因素&#xff0c;同时考虑到多平台的通用性&#xff0c;LiteOS-M选择业界标准的ELF加载方案&#xff0c;方便拓展算法生态。LiteOS-M提供类…

微信小程序认证流程

官方描述&#xff1a; 微信接口服务&#xff1a;即微信服务器。 具体的流程如下&#xff1a; 1.前端调用wx.login()获取登录凭证code 2.前端请求后端进行认证&#xff0c;发送code 3.后端请求微信获取openid 4.后端生成认证成功凭证返回给前端。 说明 调用 wx.login() 获…

【二等奖论文】2024年华为杯研赛C题54页成品论文(后续会更新)

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片&#xff0c;那是获取论文的入口&#xff01; 点击链接获取【2024华为杯研赛资料汇总】&#xff1a;https://qm.qq.com/q/Nr0POlQGc2https://qm.qq.com/q/Nr0POlQGc2 摘 要&#xff1a; 随着国民经济发…

简易CPU设计入门:取指令(一),端口列表与变量声明

取指令这一块呢&#xff0c;个人觉得&#xff0c;不太好讲。但是呢&#xff0c;不好讲&#xff0c;我也得讲啊。那就尽量地讲吧。如果讲得不好的话&#xff0c;那么&#xff0c;欢迎大家提出好的意见&#xff0c;帮助我改进讲课的质量。 首先呢&#xff0c;还是请大家去下载本…

nodejs基于vue电子产品商城销售网站的设计与实现 _bugfu

目录 技术栈具体实现截图系统设计思路技术可行性nodejs类核心代码部分展示可行性论证研究方法解决的思路Express框架介绍源码获取/联系我 技术栈 该系统将采用B/S结构模式&#xff0c;开发软件有很多种可以用&#xff0c;本次开发用到的软件是vscode&#xff0c;用到的数据库是…

FiBiNET模型实现推荐算法

1. 项目简介 A031-FiBiNET模型项目是一个基于深度学习的推荐系统算法实现&#xff0c;旨在提升推荐系统的性能和精度。该项目的背景源于当今互联网平台中&#xff0c;推荐算法在电商、社交、内容分发等领域的广泛应用。推荐系统通过分析用户的历史行为和兴趣偏好&#xff0c;预…

java项目之线上辅导班系统的开发与设计

项目简介 基于springboot的线上辅导班系统的开发与设计的主要使用者分为&#xff1a; 管理员在后台主要管理字典管理、论坛管理、公开课管理、课程管理、课程报名管理、课程收藏管理、课程留言管理、师资力量管理、用户管理、管理员管理等。 &#x1f495;&#x1f495;作者&a…

单细胞monocle3分析流程再整理

重读上一篇关于monocle3的推文的时候感觉内容冗长繁琐&#xff0c;因此笔者把关键部分代码稍作了整理。 推文链接&#xff1a;单细胞拟时序/轨迹分析monocle3流程学习和整理 https://mp.weixin.qq.com/s/NRrFH8sjdUUq20z9hWAFyQ 也可以看一看monocle2推文&#xff1a; 单细胞…

探索 ShellGPT:终端中的 AI 助手

文章目录 探索 ShellGPT&#xff1a;终端中的 AI 助手背景介绍ShellGPT 是什么&#xff1f;如何安装 ShellGPT&#xff1f;简单的库函数使用方法场景应用常见问题及解决方案总结 探索 ShellGPT&#xff1a;终端中的 AI 助手 背景介绍 在当今快速发展的技术领域&#xff0c;命…

双非本 985 硕士,秋招上岸字节算法岗!

最近已有不少大厂都在秋招宣讲了&#xff0c;也有一些在 Offer 发放阶段。 节前&#xff0c;我们邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对新人如何快速入门算法岗、如何准备面试攻略、面试常考点、大模型项目落地经验分享等热门话题进行了深入的讨论。…

Chainlit集成LlamaIndex实现知识库高级检索(自动合并检索)

检索原理 自动合并检索 自动合并检索原理&#xff0c;和我的上一篇文章的检索方案&#xff1a; 将文本分割成512大小&#xff08;一般对应段落大小&#xff09;和128&#xff08;一般对句子大小不是严格的句子长度&#xff09;大小两种分别存储到索引库&#xff0c;再用llama_…

架构设计笔记-5-软件工程基础知识

知识要点 按软件过程活动&#xff0c;将软件工具分为软件开发工具、软件维护工具、软件管理和软件支持工具。 软件开发工具&#xff1a;需求分析工具、设计工具、编码与排错工具。 软件维护工具&#xff1a;版本控制工具、文档分析工具、开发信息库工具、逆向工程工具、再工…

快速解决Isaac Sim资源获取不到问题

国内使用Isaac Sim的时候&#xff0c;最常见的问题是加载不了USD或材质资源&#xff0c;这会导致整个Isaac Sim软件卡住或崩溃&#xff0c;以及无法继续开展项目。比如加载realsense或&#xff0c;最新的Isaac Sim 4.2.0 加载一个激光雷达&#xff0c;都要获取相关传感器usd&am…

桶排序和计数排序(非比较排序算法)

桶排序 桶排序是一种基于分配的排序算法&#xff0c;特别适合用来排序均匀分布的数据。它的基本思想是将输入的数据分到有限数量的桶里&#xff0c;然后对每个桶内的数据分别进行排序&#xff0c;最后再将各个桶内的数据合并得到最终的排序结果。(通常用于浮点数&#xff0c;因…

RuntimeError: Maximum Recursion Depth Exceeded - 递归深度超限的完美解决方案

RuntimeError: Maximum Recursion Depth Exceeded - 递归深度超限的完美解决方案 &#x1f6e0;️ RuntimeError: Maximum Recursion Depth Exceeded - 递归深度超限的完美解决方案摘要 &#x1f4c3;引言 ✨1. 什么是递归&#xff1f;&#x1f50d;1.1 递归的基本概念 &#x…

JavaScript可视化示例

JavaScript 可视化是指使用 JavaScript 编程语言来创建和操作图形、图表、动画等视觉元素的过程。以下是一些常见的 JavaScript 可视化库和工具&#xff0c;以及它们的主要特点&#xff1a; 1. D3.js 特点: D3.js&#xff08;Data-Driven Documents&#xff09;是一个非常强大…

思维商业篇(4)—产业上下游定

思维商业篇(4)—产业上下游定位(微笑曲线) 产业上下游定位&#xff0c;帮助我们去观察一个企业在产业上下游中处于一个什么样的生态位。 上游 处于产业链开始端&#xff0c;百川东到海&#xff0c;百川的的起始端就是上游&#xff0c;东到海的海就是下游。 处在上游的企业一…