【c++进阶[五]】list相关接口介绍及list和vector的对比

server/2024/12/22 20:40:05/

💓博主CSDN主页::Am心若依旧💓

⏩专栏分类c++从入门到精通⏪
🚚代码仓库:青酒余成🚚

🌹关注我🫵带你学习更多c++
  🔝🔝  

1.前言

本章重点

本章重点讲解list的接口函数的熟悉,并且讲解list迭代器失效的特性和迭代器的功能分类以及算法库函数中谁能用谁不能用,最后比较一下list和vector的优势和劣势

2.list的介绍

对上诉大致理解如下:

1.list是一种可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
2.list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立结点当中,在结点中通过指针指向其前一个元素和后一个元素。
3.list与forward_list非常相似,最主要的不同在于forward_list是单链表,只能进行单方向迭代。
与其他容器相比,list通常在任意位置进行插入、删除元素的执行效率更高。
4.list和forward_list最大的缺陷是不支持在任意位置的随机访问,其次,list还需要一些额外的空间,以保存每个结点之间的关联信息(对于存储的类型较小元素来说这可能是一个重要的因素)。

更加具体的学习可以参考如下链接:list - C++ Reference (cplusplus.com)

3.list的使用

构造函数

在这里主要介绍四种构造函数

1.是无参构造,一眼就能看懂。

2.构造n个值为val的双向链表。

3.迭代器区间构造。

4.用一个初始值构造,也称之为拷贝构造函数。

上述四个构造的方法都是以int为类型,也可以使用其他的类型。具体视情况而

list<int> l1;//无参构造
list<int> l2(10,5);//用10个5初始化链表vector<int> vv{1,2,3,4,5,6};
list<int> l3(vv.begin(),vv.end());//用迭代器区间初始化//拷贝构造函数
list<char> l4('a');//用一个字符来初始化

迭代器

list底层实现迭代器的不是指针,但是可以把他当成指针来理解。list中的迭代器在底层重新封装了*,->,++,--,!=,==这几个函数。这样我们就能够像vector那样的把迭代器当成指针来使用了

这四个函数在前面讲解string和vector的时候都讲过,就不过多的叙述了。

但是一定要搞清楚这几个函数分别指向什么位置,下面用一张图帮助大家理解

begin()和rend()指向的位置相同

end()和rbegin()指向的位置相同

迭代器来遍历链表

vector<int> vv{1,3,5,7,9,11,13,15};
list<int> l(vv.begin(),vv.end());auto it = l.begin();
while(it!=l.end())
{cout << *it<< " ";it++;
}

值得注意的是:这里用的是!=而没有使用等于,而在vector中使用的是<。这是为什么呢?

这是因为在list中元素都是不连续的,如果用<的话非常有可能是错误的。而在vector中由于空间都是连续的,地址前面的一定是比后面的小的。

同时还要注意的是:这里没有使用[]来进行读取元素的值,而是使用了解引用,那是因为list不能支持随机访问,只支持解引用来读取元素。


与容量相关的函数 

 

这四个都是常见的函数了。resize在list中使用的不多,就不拿出来介绍了,有想法的可以参考上面提供的网站自己阅读。

增删查改相关函数

这几个函数一看就知道什么意思了,就不多叙述

insert插入重点讲解

使用insert函数时可以使用在某个位置插入某个值,也可以在某个位置插入n个值,也可以在某个位置前面插入一段区间大小的值

#include <iostream>
#include <algorithm>
#include <vector>
#include <list>
using namespace std;int main()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);list<int>::iterator pos = find(lt.begin(), lt.end(), 2);lt.insert(pos, 9); //在2的位置插入9for (auto e : lt){cout << e << " ";}cout << endl; //1 9 2 3pos = find(lt.begin(), lt.end(), 3);lt.insert(pos, 2, 8); //在3的位置插入2个8for (auto e : lt){cout << e << " ";}cout << endl; //1 9 2 8 8 3vector<int> v(2, 7);pos = find(lt.begin(), lt.end(), 1);lt.insert(pos, v.begin(), v.end()); //在1的位置插入2个7for (auto e : lt){cout << e << " ";}cout << endl; //7 7 1 9 2 8 8 3return 0;
}

注: find函数是头文件“algorithm”当中的一个函数,该函数在指定迭代器区间寻找指定值的位置,并返回该位置的迭代器

erase函数

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

  1. 删除指定迭代器位置的元素。
  2. 删除指定迭代器区间(左闭右开)的所有元素。
#include <iostream>
#include <algorithm>
#include <vector>
#include <list>
using namespace std;int main()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);list<int>::iterator pos = find(lt.begin(), lt.end(), 2);lt.erase(pos); //删除2for (auto e : lt){cout << e << " ";}cout << endl; //1 3 4 5pos = find(lt.begin(), lt.end(), 4);lt.erase(pos, lt.end()); //删除4及其之后的元素for (auto e : lt){cout << e << " ";}cout << endl; //1 3return 0;
}

clear函数

清空双向链表中除了头结点以外的所有节点

vector<int> vv{1,5,10,15,20,100,120};
list<int> ll(vv.begin(),vv.end());
auto pos = find(ll.begin(),ll.end(),20);
ll.insert(pos,250);
ll.erase(ll.begin()+2);
ll.clear();

clear之后就会把所有的值全部清空。(除了头结点之外,头结点可以把他理解为哨兵卫,里面的值无效)。

4.关于迭代器的问题

由于list的底层是双向带头循环链表,所以插入操作时,pos指向的节点的位置始终都是一个位置,它们只改变链接关系,并不连续,所以插入操作,并不会导致list迭代器失效。

list迭代器失效只在erase删除时才会发生

erase删除的位置在空间上是唯一的,这个位置的数据被删除后,只是改变了数据的链接关系,然而此位置已经不在原先的链表中了,再次使用会出错!

例如你删除了最后一个元素位置的值,你再对最后一个元素的迭代器位置进行解引用,那么就会报错。这就是迭代器失效的问题

因此在设计删除erase函数的时候,每当删除一个位置的时候,他都会给你返回一个有效元素的位置。

举个例子,你要把所有的偶数全部删除。按照如下的方法写就不会出现问题了。

list<int> ll{1,2,3,4,5,6,7,8};
auto it=ll.begin();
while(it!=ll.end())
{if((*it)%2==0){it=erase(it);}else{it++;}
}

5.各种迭代器的介绍

迭代器从功能可以大体分为三种:

  1. 正向迭代器: forward_iterator
  2. 双向迭代器: bidirectional_iterator
  3. 随机迭代器: random_iterator

他们分别支持++,++、--,++和--以及+和-

常见的容器以及它们的迭代器类型:

结论:

  1. 若函数模板给的随机迭代器
    则只能传有随机迭代器的容器

  2. 若函数模板给的双向迭代器
    则可以传有随机或者双向迭代器的容器

  3. 若函数模板给的单向迭代器
    则三种迭代器都可以传进来!

可以看出迭代器的优先级是:单向,双向,随机。分别依次向前兼容

6.vector与list的对比

好啦,list的相关接口就介绍到这里了,本篇文章主要介绍的是博主认为比较重要的接口,其他接口可以去下面网站自行阅读。

list::insert - C++ Reference (cplusplus.com)

                                下期预告:list的模拟实现


http://www.ppmy.cn/server/114270.html

相关文章

10.9 网络安全概述

网络安全威胁 网络安全控制技术 防火墙技术 入侵检查系统 入侵检测与防御 真题

【动态规划】回文串问题

1.回文子串 回文子串 代码&#xff1a; class Solution { public:int countSubstrings(string s) {int n s.size();vector<vector<bool>> dp(n, vector<bool>(n));int ret 0;for(int i n - 1; i > 0; i--){for(int j i; j < n; j){if(s[i] s[j]…

用Python实现时间序列模型实战——Day 15: 时间序列模型的选择与组合

一、学习内容 1. 模型选择的标准与方法&#xff08;如 AIC、BIC&#xff09; 在时间序列建模中&#xff0c;模型的选择是非常重要的&#xff0c;常用的模型选择标准包括 AIC (Akaike Information Criterion) 和 BIC (Bayesian Information Criterion)。 AIC (Akaike Informat…

手机变身无线话筒

目录 一、APP安装 二、蓝牙连接 三、使用 四、设置 近日学校给教室内安装了贝德的吸顶音响,声音洪亮不刺耳,前后排学生都清晰听到我的声音,上课轻松了很多。 不过这个360吸顶音箱使用无线耳麦,需要定时充电,我总怕没电,每次下班都给它充上电。

计算机网络10——数据库语法1

目录 1、sql语句执行顺序 2、多表查询 3、写sql的步骤 4、去重 5、视图 6、自定义函数:function 7、调用函数 1、sql语句执行顺序 一般情况下:1、from 2、where 3、select 如果有分组和having:from 分组 having最后执行 2、多表查询 内联:select * from 表1 inne…

PostgreSQL技术内幕7:PostgreSQL查询编译

文章目录 0.简介1.整体过程2.查询分析2.1 Lex2.2 Yacc2.3 PG词法分析和语法分析介绍2.4 PG语义分析 4.查询优化4.1 预处理4.1.1 提升子链接和子查询4.1.2 预处理表达式4.1.3 处理HAVING子句 4.2 改进查询树4.2.1 路径生成4.2.2 代价估计 4.3 计划生成 0.简介 一次完整的SQL执行…

一台笔记本电脑的硬件都有哪些以及对应的功能

一台笔记本电脑的硬件通常包括多个关键组件&#xff0c;这些组件共同协作&#xff0c;确保电脑的正常运行。以下是笔记本电脑的主要硬件及其功能&#xff1a; 1. 中央处理器&#xff08;CPU&#xff09; 功能&#xff1a;CPU 是电脑的“大脑”&#xff0c;负责处理所有的计算…

分类预测|基于黑翅鸢优化轻量级梯度提升机算法数据预测Matlab程序BKA-LightGBM多特征输入多类别输出 含对比

分类预测|基于黑翅鸢优化轻量级梯度提升机算法数据预测Matlab程序BKA-LightGBM多特征输入多类别输出 含对比 文章目录 一、基本原理BKA&#xff08;Black Kite Algorithm&#xff09;的原理LightGBM分类预测模型的原理BKA与LightGBM的模型流程总结 二、实验结果三、核心代码四、…