【C++学习】【STL】list容器

news2024/12/23 1:23:41

list 容器,又称双向链表容器,即该容器的底层是以双向链表的形式实现的。这意味着,list 容器中的元素可以分散存储在内存空间里,而不是必须存储在一整块连续的内存空间中。

可以看到,list 容器中各个元素的前后顺序是靠指针来维系的,每个元素都配备了 2 个指针,分别指向它的前一个元素和后一个元素。其中第一个元素的前向指针总为 null,因为它前面没有元素;同样,尾部元素的后向指针也总为 null。

基于这样的存储结构,list 容器具有一些其它容器(array、vector 和 deque)所不具备的优势,即它可以在序列已知的任何位置快速插入或删除元素(时间复杂度为O(1))。并且在 list 容器中移动元素,也比其它容器的效率高

使用 list 容器的缺点是,它不能像 array 和 vector 那样,通过位置直接访问元素。即必须要遍历到要访问的位置(正确的做法是从容器中第一个元素或最后一个元素开始遍历容器,直到找到该位置),迭代器也不能是随机访问迭代器。

实际场景中,如果需要对序列进行大量添加或删除元素的操作,而直接访问元素的需求却很少,这种情况建议使用 list 容器存储序列。

list容器的创建

    list<int> l1;//创建一个空的list容器 
    list<int> l2(10);//创建一个包含n个元素的list容器
    list<int> l3(10,5);//创建一个包含10个5的list容器
    
    //在已有list容器的情况下 
    list<int> l4(l2);//通过拷贝l2容器创建新的l4容器
    
    array<int,5>arr{11,12,13,14,15};
    list<int> l5(arr.begin()+2,arr.end());//拷贝arr容器中的{13,14,15} 

list容器可用的成员函数

C++ list(STL list)容器完全攻略(超级详细) (biancheng.net)

注意,list 容器的底层实现结构为双向链表,图 上 这种表示仅是为了方便理解各个迭代器函数的功能。

array、vector、deque 容器的迭代器,和它们相比,list 容器迭代器最大的不同在于,其配备的迭代器类型为双向迭代器,而不再是随机访问迭代器。

这意味着,假设 p1 和 p2 都是双向迭代器,则它们支持使用 ++p1、 p1++、 p1--、 p1++、 *p1、 p1==p2 以及 p1!=p2 运算符,但不支持以下操作(其中 i 为整数):

  • p1[i]:不能通过下标访问 list 容器中指定位置处的元素

  • p1-=i、 p1+=i、 p1+i 、p1-i:双向迭代器 p1 不支持使用 -=、+=、+、- 运算符

  • p1<p2、 p1>p2、 p1<=p2、 p1>=p2:双向迭代器 p1、p2 不支持使用 <、 >、 <=、 >= 比较运算符

遍历 list 容器中的各个元素。

#include <iostream>
#include <list>
using namespace std;
int main()
{
    //创建 list 容器
    std::list<char> values{'h','t','t','p',':','/','/','c','.','b','i','a','n','c','h','e','n','g','.','n','e','t'};
    //使用begin()/end()迭代器函数对输出list容器中的元素
    for (std::list<char>::iterator it = values.begin(); it != values.end(); ++it) {
        std::cout << *it;
    }
    cout << endl;
    //使用 rbegin()/rend()迭代器函数输出 lsit 容器中的元素
    for (std::list<char>::reverse_iterator it = values.rbegin(); it != values.rend();++it) {
        std::cout << *it;
    }
    return 0;
}
输出结果为:
http://c.biancheng.net
ten.gnehcnaib.c//:ptth

值得一提的是,list 容器在进行插入(insert())、接合(splice())等操作时,都不会造成原有的 list 迭代器失效,甚至进行删除操作,而只有指向被删除元素的迭代器失效,其他迭代器不受任何影响。

list容器访问元素的方法

不同于之前学过的 STL 容器,访问 list 容器中存储元素的方式很有限,即要么使用 front() 和 back() 成员函数,要么使用 list 容器迭代器

list 容器不支持随机访问,未提供下标操作符 [] 和 at() 成员函数,也没有提供 data() 成员函数。

1.通过 front() 和 back() 成员函数,可以分别获得 list 容器中第一个元素和最后一个元素的引用形式。举个例子:

#include <iostream>
#include <list>
using namespace std;
int main()
{
    std::list<int> mylist{ 1,2,3,4 };
    int &first = mylist.front();
    int &last = mylist.back();
    cout << first << " " << last << endl;
    first = 10;
    last = 20;
    cout << mylist.front() << " " << mylist.back() << endl;
    return 0;
}
输出结果为:
1 4
10 20

2.除此之外,如果想访问 list 容存储的其他元素,就只能使用 list 容器的迭代器。例如:

#include <iostream>
#include <list>
using namespace std;
int main()
{
    const std::list<int> mylist{1,2,3,4,5};
    auto it = mylist.begin();
    cout << *it << " ";
    ++it;
    while (it!=mylist.end())
    {
        cout << *it << " ";
        ++it;  
    }
    return 0;
}
运行结果为:
1 2 3 4 5

总结

访问/修改 list的头尾元素可以使用front()或back()成员函数,访问其他元素则必须要通过迭代器的遍历来实现。

list添加(插入)元素方法

list 模板类中,与“添加或插入新元素”相关的成员方法有如下几个:

push_front():向 list 容器首个元素前添加新元素;

push_back():向 list 容器最后一个元素后添加新元素;

emplace_front():在容器首个元素前直接生成新的元素;

emplace_back():在容器最后一个元素后直接生成新的元素;

emplace():在容器的指定位置直接生成新的元素;

insert():在指定位置插入新元素;

splice():将其他 list 容器存储的多个元素添加到当前 list 容器的指定位置处。

除了 insert() 和 splice() 方法有多种语法格式外,其它成员方法都仅有 1 种语法格式

#include <iostream>
#include <list>
using namespace std;
int main()
{
    std::list<int> values{1,2,3};
    values.push_front(0);//{0,1,2,3}
    values.push_back(4); //{0,1,2,3,4}
    values.emplace_front(-1);//{-1,0,1,2,3,4}
    values.emplace_back(5);  //{-1,0,1,2,3,4,5}
   
    //emplace(pos,value),其中 pos 表示指明位置的迭代器,value为要插入的元素值
    values.emplace(values.end(), 6);//{-1,0,1,2,3,4,5,6}
    for (auto p = values.begin(); p != values.end(); ++p) {
        cout << *p << " ";
    }
    return 0;
}

insert成员方法

iterator insert(pos,elem)在迭代器 pos 指定的位置之前插入一个新元素 elem,并返回表示新插入元素位置的迭代器。

iterator insert(pos,n,elem)在迭代器 pos 指定的位置之前插入 n 个元素 elem,并返回表示第一个新插入元素位置的迭代器。

iterator insert(pos,first,last) 在迭代器 pos 指定的位置之前,插入其他容器(例如 array、vector、deque 等)中位于 [first,last) 区域的所有元素,并返回表示第一个新插入元素位置的迭代器。

iterator insert(pos,initlist)在迭代器 pos 指定的位置之前,插入初始化列表(用大括号 { } 括起来的多个元素,中间有逗号隔开)中所有的元素,并返回表示第一个新插入元素位置的迭代器。

#include <iostream>
#include <list>
#include <array>
using namespace std;
int main()
{
    std::list<int> values{ 1,2 };
    //第一种格式用法
    values.insert(values.begin() , 3);//{3,1,2}
    //第二种格式用法
    values.insert(values.end(), 2, 5);//{3,1,2,5,5}
    //第三种格式用法
    std::array<int, 3>test{ 7,8,9 };
    values.insert(values.end(), test.begin(), test.end());//{3,1,2,5,5,7,8,9}
    //第四种格式用法
    values.insert(values.end(), { 10,11 });//{3,1,2,5,5,7,8,9,10,11}
    for (auto p = values.begin(); p != values.end(); ++p)
    {
        cout << *p << " ";
    }
    return 0;
}

splice()成员方法

void splice (iterator position, list& x);

position 为迭代器,用于指明插入位置;x 为另一个 list 容器。

此格式的 splice() 方法的功能是,将 x 容器中存储的所有元素全部移动当前 list 容器中 position 指明的位置处。

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

position 为迭代器,用于指明插入位置;x 为另一个 list 容器;i 也是一个迭代器,用于指向 x 容器中某个元素。

此格式的 splice() 方法的功能是将 x 容器中 i 指向的元素移动到当前容器中 position 指明的位置处。

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

position 为迭代器,用于指明插入位置;x 为另一个 list 容器;first 和 last 都是迭代器,[fist,last) 用于指定 x 容器中的某个区域。

此格式的 splice() 方法的功能是将 x 容器 [first, last) 范围内所有的元素移动到当前容器 position 指明的位置处。

#include <iostream>
#include <list>
#include <array>
using namespace std;
int main()
{
    std::list<int> values{ 1,2 };
    //第一种格式用法
    values.insert(values.begin() , 3);//{3,1,2}
    //第二种格式用法
    values.insert(values.end(), 2, 5);//{3,1,2,5,5}
    //第三种格式用法
    std::array<int, 3>test{ 7,8,9 };
    values.insert(values.end(), test.begin(), test.end());//{3,1,2,5,5,7,8,9}
    //第四种格式用法
    values.insert(values.end(), { 10,11 });//{3,1,2,5,5,7,8,9,10,11}
    for (auto p = values.begin(); p != values.end(); ++p)
    {
        cout << *p << " ";
    }
    return 0;
}

list删除元素

实现 list 容器删除元素的成员函数

pop_front()删除位于 list 容器头部的一个元素。

pop_back()删除位于 list 容器尾部的一个元素。

erase()该成员函数既可以删除 list 容器中指定位置处的元素,也可以删除容器中某个区域内的多个元素。

clear()删除 list 容器存储的所有元素。

remove(val)删除容器中所有等于 val 的元素

unique()删除容器中相邻的重复元素,只保留一份。

remove_if()删除容器中满足条件的元素

pop_front()、pop_back() 和 clear() 的用法非常简单,这里仅给出一个样例,

#include <iostream>
#include <list>
using namespace std;
int main()
{
    list<int>values{ 1,2,3,4 };
   
    //删除当前容器中首个元素
    values.pop_front();//{2,3,4}
   
    //删除当前容器最后一个元素
    values.pop_back();//{2,3}
   
    //清空容器,删除容器中所有的元素
    values.clear(); //{}
   
    for (auto begin = values.begin(); begin != values.end(); ++begin)
    {
        cout << *begin << " ";
    }
    return 0;
}
运行程序,可以看到输出结果为“空”。

erase()

erase() 成员函数有以下 2 种语法格式:

iterator erase (iterator position);

iterator erase (iterator first, iterator last);

1.第一种(可实现删除 list 容器中 position 迭代器所指位置处的元素

#include <iostream>
#include <list>
using namespace std;
int main()
{
    list<int>values{ 1,2,3,4,5 };
    //指向元素 1 的迭代器
    auto del = values.begin();
    //迭代器右移,改为指向元素 2
    ++del;
    values.erase(del); //{1,3,4,5}
    for (auto begin = values.begin(); begin != values.end(); ++begin)
    {
        cout << *begin << " ";
    }
    return 0;
}

2.第二种(可实现删除 list 容器中 first 迭代器和 last 迭代器限定区域内的所有元素

#include <iostream>
#include <list>
using namespace std;
int main()
{
    list<int>values{ 1,2,3,4,5 };
    //指定删除区域的左边界
    auto first = values.begin();
    ++first;//指向元素 2
    //指向删除区域的右边界
    auto last = values.end();
    --last;//指向元素 5
    //删除 2、3 和 4
    values.erase(first, last);
    for (auto begin = values.begin(); begin != values.end(); ++begin)
    {
        cout << *begin << " ";
    }
    return 0;
}

remove()函数:按值删除相邻重复元素

erase() 成员函数是按照被删除元素所在的位置来执行删除操作,如果想根据元素的值来执行删除操作,可以使用 remove() 成员函数

#include <iostream>
#include <list>
using namespace std;
int main()
{
    list<char>values{'a','b','c','d'};
    values.remove('c');
    for (auto begin = values.begin(); begin != values.end(); ++begin)
    {
        cout << *begin << " ";
    }
    return 0;
}

unique() 函数:可以自定义去重规则

也有以下 2 种语法格式:

void unique()

void unique(BinaryPredicate)//传入一个二元谓词函数

#include <iostream>
#include <list>
using namespace std;
//二元谓词函数
bool demo(double first, double second)
{
    return (int(first) == int(second));
}
int main()
{
    list<double> mylist{ 1,1.2,1.2,3,4,4.5,4.6 };
    //删除相邻重复的元素,仅保留一份
    mylist.unique();//{1, 1.2, 3, 4, 4.5, 4.6}
    for (auto it = mylist.begin(); it != mylist.end(); ++it)
        cout << *it << ' ';
    cout << endl;
    //demo 为二元谓词函数,是我们自定义的去重规则
    mylist.unique(demo);
    for (auto it = mylist.begin(); it != mylist.end(); ++it)
        std::cout << *it << ' ';
    return 0;
}

可以看到,通过调用无参的 unique(),仅能删除相邻重复(也就是相等)的元素,而通过我们自定义去重的规则,可以更好的满足在不同场景下去重的需求。

#include <iostream>
#include <list>
using namespace std;
int main()
{
    std::list<int> mylist{ 15, 36, 7, 17, 20, 39, 4, 1 };
    //删除 mylist 容器中能够使 lamba 表达式成立的所有元素。
    mylist.remove_if([](int value) {return (value < 10); }); //{15 36 17 20 39}
    for (auto it = mylist.begin(); it != mylist.end(); ++it)
        std::cout << ' ' << *it;
    return 0;
}

除此之外,通过将自定义的谓词函数(不限定参数个数)传给 remove_if() 成员函数,list 容器中能使谓词函数成立的元素都会被删除。

#include <iostream>
#include <list>
using namespace std;
int main()
{
    std::list<int> mylist{ 15, 36, 7, 17, 20, 39, 4, 1 };
    //删除 mylist 容器中能够使 lamba 表达式成立的所有元素。
    mylist.remove_if([](int value) {return (value < 10); }); //{15 36 17 20 39}
    for (auto it = mylist.begin(); it != mylist.end(); ++it)
        std::cout << ' ' << *it;
    return 0;
}

forward_list容器:高效率的list容器!

C++ STL forward_list容器完全攻略 (biancheng.net)

区别:forward_list底层实现使用的是单链表

效率高是选用 forward_list 而弃用 list 容器最主要的原因,换句话说,只要是 list 容器和 forward_list 容器都能实现的操作,应优先选择 forward_list 容器。

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

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

相关文章

【NodeJs】使用ffmpeg将视频webm转换为mp4

使用Chrome浏览器录制视频文件是webm格式&#xff0c;但是很多媒体播放器是不支持的&#xff0c;不利于分享&#xff0c;需要转换为mp4格式才行&#xff0c;接下来给大家讲 ffmpeg ffmpeg是什么呢&#xff0c; 一个免费开源的视频转换工具&#xff0c;一款音视频编解码工具&…

日志与可视化方案:从ELK到EFK,再到ClickHouse

EFK方案 从ELK谈起 ELK是三个开源软件的缩写&#xff0c;分别表示&#xff1a;Elasticsearch&#xff0c;Logstash&#xff0c;Kibana。新增了一个FlieBeat&#xff0c;它是一个轻量级的日志收集处理工具&#xff0c;FlieBeat占用资源少&#xff0c;适用于在各个服务器上搜集…

JS语法(扫盲)

文章目录一、初识JavaScript二、第一个JS程序JS代码的引入JS程序的输出三、语法变量使用动态类型内置类型运算符强类型语言&弱类型语言条件语句循环语句数组创建数组获取数组元素新增数组元素删除数组元素函数语法格式形参实参个数的问题匿名函数&函数表达式作用域作用…

PHP 的运行方式有哪些?

PHP本质上的运行方式可以分为两种&#xff1a; 基于命令行的基于PHP-FPM的 但实际上&#xff0c;PHP能做的事很多&#xff0c;很多场景下&#xff0c;不同的运行方式能让开发更方便&#xff0c;减轻各种工作。 测试开发 PHP内置了一个HTTP 的server。这意味着&#xff0c;很…

stm32外设-GPIO

0. 写在最前 本栏目笔记都是基于stm32F10x 1. GPIO基本介绍 GPIO—general purpose intput output 是通用输入输出端口的简称&#xff0c;简单来说就是软件可控制的引脚&#xff0c; STM32芯片的GPIO引脚与外部设备连接起来&#xff0c;从而实现与外部通讯、控制以及数据采集的…

java Date 和 Calendar类 万字详解(通俗易懂)

Date类介绍及使用关于SimpleDateFormat类Calendar类介绍及使用LocalDateTime类介绍及使用关于DateTimeFormatter类一、前言本节内容是我们《API-常用类》专题的第五小节了。本节内容主要讲Date 类 和 Calendar 类&#xff0c;内容包括但不限于Date类简介&#xff0c;Date类使用…

【微信小程序】-- 自定义组件 - 数据监听器 (三十四)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…

传奇开服流程—传奇单机架设教程

现在传奇私服还是那么的火爆&#xff0c;上次有报道发布站一年盈利几个亿&#xff0c;还是有很大的机会&#xff0c;很多玩家因为GM开服关服给折腾&#xff0c;刚充的钱服务器就关了&#xff0c;很是恼火&#xff0c;于是都想自己整个服开开&#xff0c;但又不知道从何下手&…

三菱FX5U之数据处理类指令的使用

本课程使用三菱PLC works3编程软件进行教学&#xff0c;并使用works3的仿真功能进行PLC仿真&#xff0c;学习的时候不需要有实物PLC。 补充说明&#xff1a;三菱 FX 5U系列PLC使用的是GX works3编程软件&#xff0c;FX 3U、Q系列PLC使用的是GX works3编程软件。 第一章 八个案…

YUV实践记录

文章目录YUV基础介绍&#xff1a;不同采样YUV格式的区别为什么要使用YUV格式呢&#xff1f;YUV的存储方式Android中的YUV_420_888附录&#xff1a;YUV基础介绍&#xff1a; YUV在做手机图像或者视频处理的时候会经常用到的一个格式&#xff0c;用此文来记录YUV相关介绍&#xf…

hibernate学习(五)

hibernate学习&#xff08;五&#xff09; hibernate的一对多关联映射&#xff1a; 一、数据库表与表之间关系 一对多建表原则&#xff1a; 多对多的建表原则&#xff1a; 一对一建表原则&#xff1a; &#xff08;1&#xff09;唯一外键对应&#xff1a; &#xff08;…

时间复杂度和空间复杂度的计算

目录 算法的复杂度 时间复杂的的概念 时间复杂度计算方法 大O的渐进表示法 空间复杂的概念 空间复杂的的计算方法 时间和空间复杂度的应用 消失的数字 轮转数组 算法的复杂度 算法在编写成可执行程序后&#xff0c;运行时需要耗费时间资源和空间&#xff08;内存&…

modbus转profinet网关连接5台台达ME300变频器案例

通过兴达易控Modbus转Profinet&#xff08;XD-MDPN100&#xff09;网关改善网络场景&#xff0c;变频器有掉线或数据丢失报警&#xff0c;影响系统的正常运行&#xff0c;将5台 ME300变频器modbus转Profinet到1200PLC&#xff0c;通过网关还可以实现Profinet转modbus RTU协议转…

LabVIEW中以编程方式获取VI克隆名称

LabVIEW中以编程方式获取VI克隆名称演示如何以编程方式获取VI的名称或克隆名称。如果VI作为顶级VI运行&#xff0c;则将显示VI的名称。如果VI在主VI中用作子VI&#xff0c;它将返回克隆的名称。在项目开发过程中&#xff0c;有时需要获取VI的名称。在此示例中&#xff0c;实现了…

【数论】试除法判断质数,分解质因数,筛质数

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 现已更新完KMP算法、排序模板&#xff0c;之…

代码管理--svnadmin工具介绍

1、简介 SVNAdmin2 是一款通过图形界面管理服务端SVN的web程序。正常情况下配置SVN仓库的人员权限需要登录到服务器手动修改 authz 和 passwd 两个文件&#xff0c;当仓库结构和人员权限上了规模后&#xff0c;手动管理就变的非常容易出错&#xff0c;本系统能够识别人员和权限…

【AWS入门】IAM基本应用-2023/3/4

目录IAM概述根用户和IAM用户参考IAM概述 IAM(Identity Access Management&#xff09;是身份和访问管理服务&#xff0c;要访问AWS服务和资源&#xff0c;就要使用IAM进行身份验证和授权。当我们通过控制台&#xff0c;CLI&#xff0c;或API访问AWS服务时&#xff0c;都需要通…

p5.js map映射

theme: smartblue 本文正在参加「金石计划」 本文简介 带尬猴&#xff0c;我嗨德育处主任 p5.js 为开发者提供了很多有用的方法&#xff0c;这些方法实现起来可能不难&#xff0c;但却非常实用&#xff0c;能大大减少我们的开发时间。 本文将通过举例说明的方式来讲解 映射 map…

《计算机网络》期末复习笔记

文章目录一、一些英文名词的标签&#xff08;方便记忆&#xff09;二、OSI七层协议三、综合题3.0 知识点储备3.1 在Internet 网中&#xff0c;某计算机的IP 地址是11001010.01100000.00101100.01011000 &#xff0c;请回答下列问题3.2 假定发送方要发送的数据为10000101。采用C…

【Spring 深入学习】AOP的前世今生之后续

AOP的前世今生之后续 1. 概述 上篇文章【Spring 深入学习】AOP的前世今生之代理模式我们讲述了代理模式。而我们今天的主人公AOP就是基于代理模式实现的&#xff0c;所以我们今天会简单学习下AOP 2. 什么是AOP 是面向切面编程&#xff0c;一般可以帮助我们在不修改现有代码的情…