【语法】C++的list

news2025/4/19 6:41:08

目录

为什么会有list?

迭代器失效:

 list和vector的迭代器不同的地方:


list的大部分用法和vector都很像,例如push_back,构造,析构,赋值重载这些就不再废话了,本篇主要讲的是和vector不同的点

构造、拷贝构造:

析构:

赋值重载:

尾插:

为什么会有list?

vector就可以存储多个数据了,为什么还要有一个list呢?相信这是很多初学者的问题之一,下面就给大家说清楚

vector缺点:

  1. 头部和中部的插入删除效率低(O(N)),因为需要挪动数据
  2. 插入数据空间不够需要增容,增容需要开新空间、拷贝数据、释放旧空间,会付出很大的代价

vector优点:

支持下标的随机访问,间接的就很好的支持排序、二分查找、堆算法等等

通过上面可知,vector因为支持下标的随机访问(即空间是连续的),那么再增删查改就会牺牲效率

为了解决这个痛点,就有了list

vector插入数据需要先将后面的数据往后移,如图所示

而list只需要再开辟一个新节点,并将其左右连接起来就进可以,如图所示

 但虽然list解决了vector的痛点,也同时失去了vector的优点,即随机访问

list缺点:

不支持随机访问

list优点:

  1. list头部、中部插入数据不再需要挪动数据,效率高(O(1))
  2. list插入数据是新增节点,不需要增容

所以在实际使用中vector和list是相辅相成的两个容器

 和string,vector一样,list也有迭代器,用法也一样,但string和vector在遍历数据时有三种方法,分别是[]+for、迭代器、范围for,但list中没有[]+for这个方法了,因为[]重载的前提是容器本身支持随机访问,而list不支持随机访问,自然没有[]重载

迭代器遍历:

list<int> lt;
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
lt.push_front(1);//list中不仅支持尾插和尾删,还支持头插和头删

list<int>::iterator it = lt.begin();
while (it != lt.end())
{
    cout << *it << " ";
    it++;
}

(list中不仅支持vector已有的尾插和尾删,还支持头插和头删) 

范围for遍历:(只要支持迭代器,就支持范围for)

list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);

for(auto e : lt)
{
    cout << e << " ";
}

从支持的操作接口的角度分迭代器的类型:单向(forword_list)、双向(list)、随机(vector)

迭代器失效:

那下面的代码是否可以运行呢?

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 it = lt.begin();
    while(it != lt.end())
    {
        if(*it % 2 == 0)
            lt.erase(it);
        it++;
    }
    return 0;
}

 这段程序的目的是删除链表中的偶数,但如果运行的话会报错

 原因和vector的迭代器失效类似,但上述代码list迭代器失效主要是因为都删除了这个节点了,那现在这个迭代器里指向的是一个被释放的空间,那当然不行

改也和vector的改法一样

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 it = lt.begin();
    while (it != lt.end())
    {
        if (*it % 2 == 0)
            it = lt.erase(it);
        else
            it++;//把it++放在else里是因为如果执行了erase,它的返回值就是删除的元素的下一个元素的迭代器
    }
    return 0;
}

那这段代码会发生迭代器失效吗?

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 it = lt.begin();
    while (it != lt.end())
    {
        if (*it % 2 == 0)
            lt.insert(it, 114);
        it++;
    }
    return 0;
}

答案是不会

怎么insert又不会迭代器失效了?

在vector中,insert会迭代器失效是因为原本指向这个数据的迭代器在插入数据后不是指向那个数了

而在list中,插入数据并不会改变迭代器的指向,insert后迭代器还是指向原来的数据

所以在list中,insert不会迭代器失效,erase会迭代器失效

那在vector中,插入使得扩容也会迭代器失效,list会吗?

 关于capacity的接口就只有这3个,可以看到,只有size,没有capacity,因为list不存在扩容的问题,每次插入数据都会新开辟一块空间,所以list不需要扩容,自然也不会因为扩容而迭代器失效

注:迭代器失效了没什么事,是去访问失效的迭代器就会出现问题

list还支持调出头尾数据(vector也支持)

list<int> lt;
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
lt.push_front(1);//list中不仅支持尾插和尾删,还支持头插和头删
cout << lt.front() << endl;
cout << lt.back() << endl;

输出结果:

 list还支持翻转接口

void print(const list<int>& lt)
{
    for (const auto& e : lt)
        cout << e << " ";
}

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);
    lt.reverse();
    print(lt);
    return 0;
}

 输出结果:

 list还支持排序,是的你没看错,连vector都没有内置的sort函数,list竟然有(不过效率很低,不常用)

void print(const list<int>& lt)
{
    for (const auto& e : lt)
        cout << e << " ";
}

int main()
{
    list<int> lt;
    lt.push_back(4);
    lt.push_back(2);
    lt.push_back(7);
    lt.push_back(1);
    lt.push_back(4);
    lt.sort();
    print(lt);
    return 0;
}

输出结果:

 list和vector的迭代器不同的地方:

 如果想在第三个位置的前方加入一个数据

void print(const list<int>& lt)
{
    for (const auto& e : lt)
        cout << e << " ";
}

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);
    lt.insert(lt.begin() + 2, 114);
    print(lt);
    return 0;
}

这样写可以吗?

如果是在vector的话,没问题,但list就会报错

vector支持随机访问,所以begin() + 2这种操作就是往后移两位。

但list不支持随机访问,begin() + 2 就加到未知的空间去了所以会报错

正确写法是这样 

void print(const list<int>& lt)
{
    for (const auto& e : lt)
        cout << e << " ";
}

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 it = find(lt.begin(), lt.end(), 3);
    lt.insert(it, 114);
    print(lt);
    return 0;
}

需要用到全局的算法接口find,找到begin()到end()范围(左闭右开区间)内第一个值是3的地方(从左往右走),返回值就是这个地方的迭代器

list还支持转移

它支持将链表的全部或者某一个区域的数据转移到另一个链表中

void print(const list<int>& lt)
{
    for (const auto& e : lt)
        cout << e << " ";
    cout << endl;
}

int main()
{
    list<int> lt1;
    lt1.push_back(1);
    lt1.push_back(2);
    lt1.push_back(3);
    lt1.push_back(4);
    lt1.push_back(5);
    list<int> lt2;
    lt2.push_back(114);
    lt2.push_back(514);
    lt2.push_back(191);
    lt1.splice(lt1.begin(), lt2);//将lt2的数据全部移动到lt1开始的位置
    print(lt1);
    print(lt2);//此时lt2就是空了因为都被移走了
    return 0;
}

输出结果:

第二行为空是因为lt2的数据都转移到lt1了 

remove接口也是删除一个数(用得不多)

void print(const list<int>& lt)
{
    for (const auto& e : lt)
        cout << e << " ";
    cout << endl;
}

int main()
{
    list<int> lt1;
    lt1.push_back(1);
    lt1.push_back(2);
    lt1.push_back(3);
    lt1.push_back(4);
    lt1.push_back(5);
    lt1.remove(2);//从左往右开始找值为2的数据并删掉
    print(lt1);
    return 0;
}

 但它是找AND删,而erase是删除指定的迭代器的位置

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

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

相关文章

【Ai/Agent】Windows11中安装CrewAI过程中的错误解决记录

CrewAi是什么&#xff0c;可以看之下之前写的 《初识CrewAI多智能代理团队协框架》 (注&#xff1a;这篇是基于linux系统下安装实践的) 基于以下记录解决问题后&#xff0c;可以再回到之前的文章继续进行CrewAI的安装 遇到问题 在windows系统中安装 CrewAi 不管是使用 pip 或者…

OSPF的数据报文格式【复习篇】

OSPF协议是跨层封装的协议&#xff08;跨四层封装&#xff09;&#xff0c;直接将应用层的数据封装在网络层协议之后&#xff0c;IP协议包中协议号字段对应的数值为89 OSPF的头部信息&#xff1a; 所有的数据共有的信息字段 字段名描述版本当前OSPF进程使用的版本&#xff08;…

【力扣刷题实战】Z字形变换

大家好&#xff0c;我是小卡皮巴拉 文章目录 目录 力扣题目&#xff1a;Z字形变换 题目描述 解题思路 问题理解 算法选择 具体思路 解题要点 完整代码&#xff08;C&#xff09; 兄弟们共勉 &#xff01;&#xff01;&#xff01; 每篇前言 博客主页&#xff1a;小卡…

力扣题解:142. 环形链表 II

在链表学习中&#xff0c;我们已经了解了单链表和双链表&#xff0c;两者的最后一个结点都会指向NULL&#xff1b;今天我们介绍的循环列表则不同&#xff0c;其末尾结点指向的这是链表中的一个结点。 循环链表是一种特殊类型的链表&#xff0c;其尾节点的指针指向头节点&#…

latex模板文件

LaTeX 是一款广泛应用于学术领域的​​文档排版系统​​&#xff0c;尤其以其在数学公式、科学符号和复杂技术文档排版中的强大能力著称。虽然它本身并非专门的“数学软件”&#xff0c;但在处理数学相关内容时表现尤为出色。 1. LaTeX 的核心特点​ 数学公式支持​​&#xff…

BLE 协议栈事件驱动机制详解

在 BlueNRG-LP 等 BLE 系统中,事件驱动是控制状态转移、数据交互和外设协作的基础。本文将深入讲解 BLE 协议栈中事件的来源、分发流程、处理结构与实际工程实践策略,帮助你构建稳定、可维护的 BLE 系统。 📦 一、BLE 事件的来源分类 BLE 协议栈中的事件严格来自协议栈本身…

Rust 之四 运算符、标量、元组、数组、字符串、结构体、枚举

概述 Rust 的基本语法对于从事底层 C/C 开发的人来说多少有些难以理解&#xff0c;虽然官方有详细的文档来介绍&#xff0c;不过内容是相当的多&#xff0c;看起来也费劲。本文通过将每个知识点简化为 一个 DEMO 每种特性各用一句话描述的形式来简化学习过程&#xff0c;提高学…

fuse-python使用fuse来挂载fs

winfsp 安装winfsp,https://winfsp.dev/ fusepy python安装fusepy #!/usr/bin/env python3 import os import stat from fuse import FUSE, FuseOSError, Operationsclass Passthrough(Operations):def __init__(self, root):self.root root# 辅助函数&#xff1a;将挂载点…

基于ueditor编辑器的功能开发之增加自定义一键排版功能

用户有自己的文章格式&#xff0c;要求复制或者粘贴进来的文章能够一键排版&#xff0c;不需要手动调试 这个需求的话咱们就需要自己去注册一个事件啦&#xff0c;这里我没有修改源码&#xff0c;而是在编辑器初始化之后给他注册了一个事件 我的工具列表变量 vue组件中data中…

VS Code Markdown渲染配置

VS code markdown preview enhanced插件渲染配置 mac: commandshiftP命令输入Markdown Preview Enhanced: Customize CSS&#xff0c;并点击在打开的style.less配置文件添加一下配置 /* Please visit the URL below for more information: */ /* https://shd101wyy.github.…

数据结构篇:线性表的另一表达—链表之单链表(上篇)

目录 1.链表的引入 1.1 链表的概念 1.2 next的意义 2.链表的分类 3.单链表的实现 3.1 单链表实现接口 3.1.1 插入节点函数封装 3.1.2 尾插 3.1.3 头插 3.1.4 报错的根本问题 3.1.5 头删 3.1.6 尾删 4.小结 1.链表的引入 根据顺序表的一些缺陷…

SpringBoot企业级开发之【用户模块-获取用户详细信息】

接口文档的要求&#xff1a; 了解一下token令牌头是怎么用的 我们直接放到前端交互的controller类下&#xff0c;在声明的方法中加入参数为String token且加入注解RequestHeader(name"Authorization【你自己设定的token】") 设计思路: 实战开发&#xff1a; control…

Mockito如何对静态方法进行测试

在 Mockito 中,直接对静态方法进行模拟是困难的,因为 Mockito 的设计理念是优先通过依赖注入(DI)管理对象,而静态方法破坏了这种设计(难以解耦)。不过,从 Mockito 3.4.0 版本开始,通过 mockStatic 方法支持了对静态方法的模拟(需配合 mockito-inline 依赖)。 从 Mo…

Navicat 17 for Mac 数据库管理

Navicat 17 for Mac 数据库管理 一、介绍 Navicat Premium 17 for Mac是一款专业的数据库管理工具&#xff0c;适用于开发人员、数据库管理员和分析师等用户。它提供了强大的数据管理功能和丰富的工具&#xff0c;使用户能够轻松地管理和维护数据库&#xff0c;提高数据处理效…

grok 驱动级键盘按键记录器分析

grok是一个驱动模块&#xff0c;其主要功能就行进行键盘按键及剪切板数据的记录&#xff0c;也就是一个键盘记录器。实现原理是通过对shadow-ssdt的相关函数进行hook,和r3对GetUserMessage进行hook的原理差不多。 关键部分如下&#xff1a; 查找csrss.exe进程是否已经启动&…

MyBatis中特殊符号处理总结

前言 MyBatis 是一款流行的Java持久层框架&#xff0c;广泛应用于各种类型的项目中。因为我们在日常代码 MyBatis 动态拼接语句时&#xff0c;会经常使用到 大于(>,>)、小于(<,<)、不等于(<>、!)操作符号。由于此符号包含了尖括号&#xff0c;而 MyBatis 使用…

MYSQL——SQL语句到底怎么执行

查询语句执行流程 MySQL 查询语句执行流程 查询缓存&#xff08;Query Cache&#xff09; MySQL内部自带了一个缓存模块&#xff0c;默认是关闭的。主要是因为MySQL自带的缓存应用场景有限。 它要求SQL语句必须一摸一样表里面的任何一条数据发生变化时&#xff0c;该表所有缓…

智能血压计WT2801芯片方案-BLE 5.0无线传输、高保真语音交互、LED显示驱动、低功耗待机四大技术赋能

在智能健康设备飞速发展的今天&#xff0c;血压计早已不再是简单的“测量工具”&#xff0c;而是家庭健康的“智能管家”。然而&#xff0c;一台真正可靠、易用、功能全面的血压计&#xff0c;离不开一颗强大的“核心芯片”。 今天&#xff0c;我们揭秘医疗级芯片WT2801的硬核实…

基于51单片机的智能火灾报警系统—温度烟雾检测、数码管显示、手动报警

基于51单片机的火灾报警系统 &#xff08;仿真&#xff0b;程序&#xff0b;原理图&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 由51单片机MQ-2烟雾传感ADC0832模数转换芯片DS18B20温度传感器数码管显示按键模块声光报警模块构成 具体功能&#xff1a;…

指定运行级别

linux系统下有7种运行级别,我们需要来了解一下常用的运行级别,方便我们熟悉以后的部署环境,话不多说,来看. 开机流程&#xff1a; 指定数级别 基本介绍 运行级别说明: 0:关机 相当于shutdown -h now ⭐️默认参数不能设置为0,否则系统无法正常启动 1:单用户(用于找回丢…