【C++】—— list 的了解与使用

【C++】—— list 的了解与使用

  • 1 list 的函数接口
  • 2 迭代器
    • 2.1 简单使用 list 的迭代器
    • 2.2 迭代器的划分
    • 2.3 不同迭代器的使用场景
      • 2.3.1 sort
      • 2.3.2 reverse
      • 2.3.3 find
  • 3 emplace_back
  • 4 操作函数
    • 4.1 sort
      • 4.1.1 list中sort介绍
      • 4.1.2 list 中 sort 与算法库中 sort 效率比较
    • 4.2 merge
    • 4.3 unique
    • 4.4 remove
    • 4.5 splice

1 list 的函数接口

  STL库中的 l i s t list list,其底层是一个带头双向循环链表

  STL库 的容器都有很强的相似性,我们有了前面 s t r i n g string string v e c t o r vector vector 的基础,再来学习 l i s t list list 就会轻松很多。
  我把 l i s t list list 的函数接口放出来,相信都加看一眼就知道如何使用了。

在这里插入图片描述

成员函数

在这里插入图片描述

迭代器

在这里插入图片描述

容量

在这里插入图片描述

元素访问

在这里插入图片描述

修改

  上述接口我们都很熟悉了,本文就不再一一进行介绍,大家自行查阅文档即可


  但是关于操作的接口我们还比较陌生,等下我们会一一学习

在这里插入图片描述

操作

  
  

2 迭代器

2.1 简单使用 list 的迭代器

  我们先来简单使用一下 l i s t list list 的迭代器

void test1()
{list<int> l1;l1.push_back(1);l1.push_back(2);l1.push_back(3);l1.push_back(4);l1.push_back(5);list<int>::iterator it = l1.begin();while (it != l1.end()){cout << *it << endl;++it;}
}

运行结果:

在这里插入图片描述

  可以看到, l i s t list list 的迭代器访问和 s t r i n g string string v e c t o r vector vector 等是一样的。

  那么 l i s t list list 的迭代器可不可以使用 + 呢?

l1.insert(l1.begin() + 3, 10);

不可以的!

  那既然不可以直接 +3,我们想在第三个位置之前插入数据怎么办呢?

方法如下:

void test1()
{list<int> l1;l1.push_back(1);l1.push_back(2);l1.push_back(3);l1.push_back(4);l1.push_back(5);auto it = l1.begin();int k = 3;while (k--){++it;}l1.insert(it, 10);for (auto e : l1){cout << e << " ";}cout << endl;	
}

运行结果:

在这里插入图片描述

  
  

2.2 迭代器的划分

  这里,我先来给大家介绍一下迭代器的划分

功能上进行划分

  • iterator
  • reverse_iterator
  • const_iterator
  • const_reverse_iterator

性质上进行划分

  • 单向迭代器 f o r w a d forwad forwad l i s t list list / u n o r d e r e d unordered unordered m a p map map / u n o r d e r e d unordered unordered_ s e t set set 等为单向迭代器。他们只支持 ++
  • 双向迭代器 l i s t list list / m a p map map / s e t set set 等都是双向迭代器。他们支持++/–,但不支持+/-。
  • 随机迭代器 v e c t o r vector vector / s t r i n g string string / d e q u e deque deque 等都是随机迭代器。他们支持++/–/+/-
  • 决定迭代器的性质的是容器的底层结构。

   l i s t list list 的迭代器是一个双向迭代器

在这里插入图片描述

  
  

2.3 不同迭代器的使用场景

  决定迭代器的性质的是容器的底层结构。而底层结构也决定了容器可以使用那些算法
  

2.3.1 sort

在这里插入图片描述

  用 s o r t sort sort 算法进行排序,并不是传什么迭代器都可以的,必须传随机迭代器,否则就会报错。
  为什么呢?因为 s o r t sort sort 底层是快速排序。快排里面要做类似三数取中的东西,它需要支持随机访问。
  

2.3.2 reverse

在这里插入图片描述

   r e v e r s e reverse reverse 是将指定范围内的数据进行翻转。

在这里插入图片描述

  这个算法底层用了 '-'操作符,因此要传双向迭代器

  那 v e c t o r vector vector s t r i n g string string 这些随机迭代器能用逆置算法吗?
  肯定。因为他们功能上是一个包含的关系,双向是一种特殊的单向,随机是一种特殊的双向
  

2.3.3 find

在这里插入图片描述

  那 f i n d find find 算法又需传递什么迭代器呢?
  InputIterator 是什么迭代器呢?它不是单向,双向,随机种的任意一种

这里我们再简单介绍一下:

在这里插入图片描述

  库里面对迭代器性质的定义使用了 C++ 中继承的概念,继承就是子类是一个特殊的父类,所以它认为这里的单向、双向、随机是一个继承关系随机是一个特殊的双向 ,双向是一个特殊的单向
  同时,库中引申出了两个不存在的迭代器只读和只写迭代器(严格来说没有容器的迭代器类型对应这两类,是抽象的概念)。InputIterator迭代器 意味着单向、双向、随机都是其子类。
  出现InputIterator就说明可以给任意类型的迭代器
  
  这里因为 f i n d find find 底层只用了 ++

在这里插入图片描述

  
  

3 emplace_back

  现阶段,我们可以认为 push_backemplace_back 是一样的,都是尾插一个数据

void test2()
{list<int> l1;l1.emplace_back(1);l1.emplace_back(2);l1.emplace_back(3);l1.emplace_back(4);l1.emplace_back(5);list<int>::iterator it = l1.begin();while (it != l1.end()){cout << *it << endl;++it;}
}

运行结果:

在这里插入图片描述

  但是在用法上,如果插入数据的参数是多个的,emplace_back效率会高一些
  
假设我们现在要插入自定义类型 A

struct A
{A(int a1 = 1, int a2 = 1):_a1(a1),_a2(a2){}int _a1;int _a2;
};

  

对应插入自定义类型 A 有两种常见的方法

  • 插入有名对象
  • 插入匿名对象
void test3()
{list<A> lt;A aa1(1, 1);lt.push_back(aa1);lt.push_back(A(2, 2));lt.emplace_back(aa1);lt.emplace_back(A(3, 3));
}

  这两种方式 p u s h push push b a c k back back e m p l a c e emplace emplace b a c k back back 都支持
  
  但是 emplace_back 还支持这样写:

void test4()
{list<A> lt;lt.emplace_back(3, 3);
}

   e m p l a c e emplace emplace_ b a c k back back 支持直接传构造 A 的参数。
  这里不是隐式类型转换,现阶段我们先不管其底层,学会用法即可。
  
  

4 操作函数

4.1 sort

4.1.1 list中sort介绍

在这里插入图片描述

  我们前面看到,算法库中的 sort 要求是随机迭代器 l i s t list list 的迭代器并不符合,因此它自己实现了一个排序算法。和算法库中的 s o r t sort sort 一样, l i s t list list 中的 s o r t sort sort 排序默认排的是升序,如果要排降序则要用到仿函数(后面会讲)。
   l i s t list list 实现的 s o r t sort sort 底层用的是归并排序,而算法库中的 s o r t sort sort 底层用的是快速排序

void test5()
{list<int> lt;lt.push_back(3);lt.push_back(5);lt.push_back(4);lt.push_back(1);lt.push_back(2);lt.sort();for (auto e : lt){cout << e << " ";}cout << endl;lt.sort(greater<int>());for (auto e : lt){cout << e << " ";}cout << endl;
}

运行结果:

在这里插入图片描述

  

4.1.2 list 中 sort 与算法库中 sort 效率比较

  虽然 l i s t list list 中实现了 s o r t sort sort 排序算法,但是它的排序效率很低,尽量不要用它来排序。

  我们将他与算法库中的 s o r t sort sort 进行性能对比

  • 我们产生 1000000 个相同的数,放进 v e c t o r vector vector l i s t list list 中,再分别用算法库中 s o r t sort sort,和 l i s t list list s o r t sort sort 排序,看运行时间
void test10()
{srand(time(0));const int N = 1000000;list<int> lt1;vector<int> v;for (int i = 0; i < N; ++i){auto e = rand() + i;lt1.push_back(e);v.push_back(e);}int begin1 = clock();// 排序sort(v.begin(), v.end());int end1 = clock();int begin2 = clock();lt1.sort();int end2 = clock();printf("vector sort:%d\n", end1 - begin1);printf("list sort:%d\n", end2 - begin2);
}

运行结果:

在这里插入图片描述

  差距 2 倍多

  • 这一次我们用两个链表进行排序:一个链表将数据拷贝进 v e c t o r vector vector,用库中的 s o r t sort sort 排序,后再拷贝回来;另一个直接调用 l i s t list list 中的 s o r t sort sort
void test11()
{srand(time(0));const int N = 1000000;list<int> lt1;list<int> lt2;for (int i = 0; i < N; ++i){auto e = rand() + i;lt1.push_back(e);lt2.push_back(e);}int begin1 = clock();// 拷贝vectorvector<int> v(lt2.begin(), lt2.end());// 排序sort(v.begin(), v.end());// 拷贝回lt2lt2.assign(v.begin(), v.end());int end1 = clock();int begin2 = clock();lt1.sort();int end2 = clock();printf("list copy vector sort copy list sort:%d\n", end1 - begin1);printf("list sort:%d\n", end2 - begin2);
}

  


  上述代码用了 l i s t list list 中的 a s s i g n assign assign 接口,我们简单介绍一下

在这里插入图片描述

   a s s g i n assgin assgin 功能是用一段迭代器区间对调用函数的 l i s t list list 对象进行拷贝,任何容器的迭代器都可以。像 v e c t o r vector vector 等都有 a s s g i n assgin assgin 接口

运行结果:

在这里插入图片描述

  可以看到,即使多了两次拷贝,依然是算法库中的 sort 效率更高。同时这也说明了拷贝的代价其实是很低的
  

4.2 merge

在这里插入图片描述

   m e r g e merge merge 接口的功能是:将两个链表合并。合并的前提是两个链表是有序的。

void test6()
{std::list<double> first, second;first.push_back(3.1);first.push_back(2.2);first.push_back(2.9);second.push_back(3.7);second.push_back(7.1);second.push_back(1.4);first.sort();second.sort();first.merge(second);for (auto e : first){cout << e << endl;}
}

运行结果:

在这里插入图片描述

  需要注意:合并会让其中一个链表空掉。
  其底层是创建一个新链表,依次去两个链表中小的尾插,最后再将新链表与first链表进行交换。

  

4.3 unique

在这里插入图片描述

   u n i q u e unique unique 是一个去重接口,但它要求数据必须是有序的

void test7()
{std::list<int> lt;lt.push_back(3);lt.push_back(3);lt.push_back(1);lt.push_back(2);lt.push_back(4);lt.push_back(4);lt.push_back(5);lt.push_back(5);lt.push_back(1);lt.push_back(5);lt.push_back(2);for (auto e : lt){cout << e << " ";}cout << endl;lt.sort();lt.unique();for (auto e : lt){cout << e << " ";}cout << endl;	
}

运行结果:

在这里插入图片描述

  

4.4 remove

在这里插入图片描述

   r e m o v e remove remove 接口是对一个值进行删除,他和 e r a s e erase erase 有点像。不同的是 e r a s e erase erase 是传一个迭代器 r e m o v e remove remove 是传一个,我找到了我就删,没找到也不会报错

  

4.5 splice

在这里插入图片描述

   s p l i c e splice splice 是粘贴的意思,但这函数接口功能更像剪切
   s p l i c e splice splice 的功能本质是一种转移 x x x 链表中的元素转移到调用该函数的 p o s i t i o n position position 位置之前(是转移不是粘贴)
  
实例:将 m y l i s t 2 mylist2 mylist2 数据转移到 m y l i s t 1 mylist1 mylist1 中 2 的前面

void test8()
{std::list<int> mylist1, mylist2;std::list<int>::iterator it;// set some initial values:for (int i = 1; i <= 4; ++i)mylist1.push_back(i);      // mylist1: 1 2 3 4for (int i = 1; i <= 3; ++i)mylist2.push_back(i * 10);   // mylist2: 10 20 30it = mylist1.begin();++it;							// points to 2mylist1.splice(it, mylist2);	// mylist1: 1 10 20 30 2 3 4// mylist2 (empty)// "it" still points to 2 (the 5th element)cout << "mylist1:";for (auto e : mylist1){cout << e << " ";}cout << endl;cout << "mylist2:";for (auto e : mylist2){cout << e << " ";}cout << endl;
}

运行结果:

在这里插入图片描述

  
   s p l i c e splice splice 还有以下用法:调整当前链表节点的顺序
  比如我想将第一个链表中的 5 放在第一个,怎么做呢

void test9()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);lt.push_back(6);}

  第一种方法就是将 5 这个节点删除,再进行头插。但这一来一去又是删除又是 n e w new new 的,效率较低。
  我们还有一种方法:运用 splice 接口 s p l i c e splice splice 允许自己转移给自己。

void test9()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);lt.push_back(6);list<int>::iterator it = find(lt.begin(), lt.end(), 5);if (it != lt.end()){lt.splice(lt.begin(), lt, it);}for (auto e : lt){cout << e << " ";}cout << endl;
}

运行结果:

在这里插入图片描述

  
  不仅如此,我们函转移一段范围

list<int>::iterator it = find(lt.begin(), lt.end(), 4);
if (it != lt.end())
{lt.splice(++lt.begin(), lt, it, lt.end());
}

运行结果:

在这里插入图片描述

  
  
  
  
  


  好啦,本期关于 l i s t list list 的知识就介绍到这里啦,希望本期博客能对你有所帮助。同时,如果有错误的地方请多多指正,让我们在 C++ 的学习路上一起进步!

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

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

相关文章

Web:HTTP包的相关操作

目录 一、请求包修改页面来源 二、Cookie身份认证 三、XXF修改本地访问 四、向页面同时发出GET和POST请求 一、请求包修改页面来源 题目提示要从 http://localhost:8080/flag3cad.php?a1&#xff0c;请求包中没有指定请求来源&#xff0c;需要指定。 而表示页面来源的字段…

华南医电科技集团受邀出席中马建交50周年高级别经贸合作交流活动

左:马来西亚第九任首拿督斯里 伊斯迈尔沙必里雅各布; 右:华南医电董事长陈广元 在庆祝中国和马来西亚建交50周年的辉煌时刻,中马两国间的经贸合作不仅承载着历史的重任,更展望着未来无限的广阔前景。2024年,作为这一重要里程碑的纪念之年,中马两国政府及商界精英携手举办了一…

解决项目启动时报“找不到符号”问题

前言 在Java开发过程中&#xff0c;遇到“找不到符号”的错误是非常常见的现象。这种错误往往意味着编译器无法识别你所引用的某个类、方法或变量。本文旨在提供一套详细的排查和解决思路&#xff0c;帮助开发者快速定位并解决此类问题。 问题描述 “找不到符号”错误通常出…

Ubuntu下安装最新版本Apache2文件服务器

文章目录 1.最新版本Apache2安装2. Apache2配置2.1 端口配置2.2 创建软连接,生成文件服务2.3 隐藏Apache2服务版本号2.4 添加用户&#xff0c;设置Apache2文件服务密码2.5 重启Apache2服务3. 执行后效果 1.最新版本Apache2安装 注意&#xff1a;安装最新版本必须升级Ubuntu为20…

网络药理学:15、草稿暂存区

TCMSP 韦恩图在线网站 https://bioinfogp.cnb.csic.es/tools/venny/index.html String数据库参数详解&#xff1a;https://www.bilibili.com/video/BV1q64y1k7Zf?p16&vd_sourceaed4c634975918b14b7354ec93ce5389 David数据库可以用基因ID或者基因名。 KEGG数据库使用&am…

linux环境下手动安装mysql

没想到兜兜转转这么些年&#xff0c;今天申请个云服务器用来搭建求生2服务器&#xff0c;先用mysql来测试&#xff0c;结果还是花了相当久的时间。 基本所有单节点部署应用到linux环境&#xff0c;都三个流程&#xff1a; 1 下载安装包 2 解压修改配置文件 3 运行启动脚本 我们…

2024年最新软件测试学习路线图(从入门到精通)

六维全息课程注重综合能力培养&#xff0c;从入学到职后一站式服务测试开发人才。2024年最新软件测试学习路线图&#xff0c;从入门到精通一应俱全。 9阶段专业课11大专项测试项目 适应互联网企业测试开发需求。 对于想入行学软件测试的新手来说&#xff0c;首先就需要一个高效…

Qt自定义信号、带参数的信号、lambda表达式和信号的使用

整个部分知识通过一个跳转窗口的项目来体现 第一个页面 #include "test.h" #include <qdebug.h> test::test(QWidget *parent): QDialog(parent) {ui.setupUi(this);/** &s 信号发出者* &subWidget::mySignals 处理的信号&#xff0c; &发送者类…

携手鲲鹏,长亮科技加速银行核心系统升级

新经济周期下&#xff0c;银行净息差持续收窄、盈利压力加大、市场竞争日趋加剧。同时&#xff0c;国家相关政策不断出台&#xff0c;对金融科技的自主创新与安全可控提出了更高要求。 在这样的大背景下&#xff0c;银行业的数字化转型已经步入深水区。其中&#xff0c;核心系统…

Games101学习 - 光栅化

Games101中讲解的光栅化的基础知识&#xff0c;本文就来梳理一下。 在UE中使用UTexture2D可以逐像素绘制纹理&#xff1a; https://blog.csdn.net/grayrail/article/details/142165442 1.绘制三角形 这里可以通过101中讲解的叉积法逐像素绘制三角形&#xff1a; 绘制效果&a…

表单标记form

1.form:表单域标记&#xff0c;表示表单范围&#xff0c;所有的表单元素必须放进form标记中 2.input:用来设置表单输入元素&#xff0c;<input>元素根据不同的属性&#xff0c;可以有多种形式&#xff0c;如文本框&#xff08;text&#xff09;,密码框&#xff08;passw…

信息安全数学基础(9)素数的算数基本定理

前言 在信息安全数学基础中&#xff0c;素数的算数基本定理&#xff08;也称为唯一分解定理或算术基本定理&#xff09;是一个极其重要的定理&#xff0c;它描述了正整数如何唯一地分解为素数的乘积。这个定理不仅是数论的基础&#xff0c;也是许多密码学算法&#xff08;如RSA…

Java面试篇基础部分-Java泛型详解

导语   Java中泛型的本质是参数化类型,泛型提供了编译时类型的安全检测机制。泛型机制允许程序在编译的时候检测非法的类型,例如要实现一个对于字符串、整型、浮点型、对象类型等比较其大小的方法,就可以使用泛型,在使用的时候在明确所要比较的数据类型就可以了。 当然如…

OAExploit一款基于OA产品的一键扫描工具

OAExploit一款基于OA产品的一键扫描工具 01 项目介绍 一款扩展性高的渗透测试框架渗透测试框架 出现卡死的几种情况&#xff1a;1.点击按钮太快 2. 打印log 的异常 02 工具展示

【有啥问啥】复习变分下界即证据下界(Evidence Lower Bound, ELBO):原理与应用

复习变分下界即证据下界&#xff08;Evidence Lower Bound, ELBO&#xff09;&#xff1a;原理与应用 变分下界&#xff08;Variational Lower Bound&#xff09;&#xff0c;也称为“证据下界”&#xff08;Evidence Lower Bound, ELBO&#xff09;&#xff0c;是概率模型中的…

git编译安装报错

编译安装步骤 卸载旧的 yum -y remove gitcd /usr/local/src/wget https://www.kernel.org/pub/software/scm/git/git-2.15.1.tar.xztar -vxf git-2.15.1.tar.xzcd git-2.15.1make prefix/usr/local/git allmake prefix/usr/local/git installecho "export PATH$PATH:/usr…

c#中给winform定义快捷键的几种方式

快捷键的使用在日常的开发中频率比较高&#xff0c;这里总结了最常见的各种快捷键的设置方式&#xff0c;需要的时候大家直接照抄就可以了&#xff0c;不用再去查询如何实现了。 文章目录 一、按钮快捷键二、菜单快捷键三、全局快捷键1、重写ProcessCmdKey2、使用KeyPreview属…

操作系统的重点笔记-1

一、操作系统的设计目标 1.易用性 使计算机易于使用&#xff0c;提供文件抽象后&#xff0c;对文件的操作就是对磁盘的操作&#xff0c;不再需要考虑如何通过控制磁盘移动&#xff0c;实现对磁盘某个信号的读写细节 2.高效性 完成特定功能的效率&#xff0c;如时间效率&…

Golang | Leetcode Golang题解之第404题左叶子之和

题目&#xff1a; 题解&#xff1a; func isLeafNode(node *TreeNode) bool {return node.Left nil && node.Right nil }func sumOfLeftLeaves(root *TreeNode) (ans int) {if root nil {return}q : []*TreeNode{root}for len(q) > 0 {node : q[0]q q[1:]if no…

Win11 频繁蓝屏重启

一、问题描述 最近在使用笔记本的时候时不时的蓝屏重启&#xff0c;甚至重启完进系统立马蓝屏重启&#xff0c;还好我凭借快速的手速拍到了错误的原因&#xff0c;如下图所示。 失败的操作是Netwtw12.sys&#xff0c;查了一下这个错误是由于无线网卡导致的&#xff0c;经过测试…