STL-list容器的使用
在C++标准库中,std::list 是一个双向链表容器,提供高效的插入和删除操作,尤其适用于需要频繁在容器中间进行插入和删除元素的场景。与其他序列容器(如 std::vector 和 std::deque)相比,std::list 有其独特的优势和使用场景。
目录
- 简介
- 主要特点
- 基本用法
- 声明和初始化
- 常用操作
- 迭代器
- 性能比较
- 高级功能
- splice
- merge
- sort
- reverse
- 使用场景
- 示例代码
- 注意事项
- 总结
简介
std::list 是C++标准模板库(STL)中的一个容器,基于双向链表实现。它允许在任意位置高效地插入和删除元素,但不支持随机访问(如通过索引访问元素)。适用于需要频繁进行中间插入和删除操作,而不需要快速随机访问的场景。
主要特点
- 双向链表:每个元素包含指向前后元素的指针,支持双向遍历。
- 动态大小:容器大小可根据需要动态调整,无需预先分配。
- 高效的插入和删除:在任何位置插入或删除元素的时间复杂度为常数时间
O(1),前提是已知位置。 - 不支持随机访问:无法像
std::vector那样通过索引直接访问元素。 - 稳定的迭代器:插入和删除操作不会使迭代器失效,除非删除了迭代器所指向的元素。
基本用法
声明和初始化
#include <iostream>
#include <list>
#include <string>int main() {// 声明一个空的 liststd::list<int> intList;// 使用初始化列表初始化std::list<std::string> stringList = {"Apple", "Banana", "Cherry"};// 使用拷贝构造函数std::list<std::string> copiedList(stringList);// 使用指定数量和初始值初始化std::list<int> filledList(5, 100); // 包含5个100return 0;
}
常用操作
插入元素
- push_back 和 push_front:在末尾和开头插入元素。
- insert:在指定位置插入元素。
#include <list>
#include <iostream>int main() {std::list<int> myList = {1, 2, 3};// 在末尾插入myList.push_back(4); // {1, 2, 3, 4}// 在开头插入myList.push_front(0); // {0, 1, 2, 3, 4}// 在指定位置插入auto it = myList.begin();++it; // 指向1myList.insert(it, 100); // {0, 100, 1, 2, 3, 4}// 输出列表内容for (const auto& num : myList) {std::cout << num << " ";}// 输出: 0 100 1 2 3 4return 0;
}
删除元素
- pop_back 和 pop_front:删除末尾和开头元素。
- erase:删除指定位置的元素。
- remove:删除所有匹配值的元素。
#include <list>
#include <iostream>int main() {std::list<int> myList = {0, 100, 1, 2, 3, 4};// 删除末尾元素myList.pop_back(); // {0, 100, 1, 2, 3}// 删除开头元素myList.pop_front(); // {100, 1, 2, 3}// 删除特定元素(通过迭代器)auto it = myList.begin();++it; // 指向1myList.erase(it); // {100, 2, 3}// 删除所有值为100的元素myList.remove(100); // {2, 3}// 输出列表内容for (const auto& num : myList) {std::cout << num << " ";}// 输出: 2 3return 0;
}
访问元素
由于 std::list 不支持随机访问,因此不能使用下标操作符 []。可以使用迭代器或范围 for 循环访问元素。
#include <list>
#include <iostream>int main() {std::list<std::string> fruits = {"Apple", "Banana", "Cherry"};// 使用迭代器访问for (std::list<std::string>::iterator it = fruits.begin(); it != fruits.end(); ++it) {std::cout << *it << " ";}// 输出: Apple Banana Cherry// 使用范围 for 循环for (const auto& fruit : fruits) {std::cout << fruit << " ";}// 输出: Apple Banana Cherryreturn 0;
}
修改元素
#include <list>
#include <iostream>int main() {std::list<int> numbers = {1, 2, 3, 4, 5};// 修改所有元素for (auto& num : numbers) {num *= 10; // {10, 20, 30, 40, 50}}// 输出修改后的列表for (const auto& num : numbers) {std::cout << num << " ";}// 输出: 10 20 30 40 50return 0;
}
迭代器
std::list 提供双向迭代器(std::list::iterator 和 std::list::const_iterator),允许从前向后或从后向前遍历元素。
#include <list>
#include <iostream>int main() {std::list<int> myList = {1, 2, 3, 4, 5};// 前向迭代std::cout << "前向迭代: ";for (std::list<int>::iterator it = myList.begin(); it != myList.end(); ++it) {std::cout << *it << " ";}// 输出: 1 2 3 4 5// 反向迭代std::cout << "\n反向迭代: ";for (std::list<int>::reverse_iterator rit = myList.rbegin(); rit != myList.rend(); ++rit) {std::cout << *rit << " ";}// 输出: 5 4 3 2 1return 0;
}
性能比较
std::list vs std::vector
| 特性 | std::list | std::vector |
|---|---|---|
| 随机访问 | 不支持 | 支持 O(1) 时间复杂度 |
| 插入/删除 | 中间插入/删除 O(1)(已知位置) | 中间插入/删除 O(n) |
| 插入/删除(末尾) | O(1) | 平均 O(1),摊销 |
| 内存使用 | 每个元素需要额外的指针 | 更紧凑,元素连续存储 |
| 缓存局部性 | 差 | 良好,连续存储提高缓存命中率 |
| 遍历 | 线性时间 | 线性时间,缓存友好 |
| 空间效率 | 较低 | 较高 |
选择建议
-
使用
std::list:- 需要频繁在容器中间插入和删除元素。
- 不需要随机访问元素。
- 需要稳定的迭代器,不希望插入/删除操作导致迭代器失效。
-
使用
std::vector:- 需要快速随机访问元素。
- 插入和删除操作主要在末尾进行。
- 更好的缓存性能,提高遍历效率。
高级功能
splice
splice 方法用于在两个 std::list 之间移动元素,无需复制或移动元素本身,效率极高。
#include <list>
#include <iostream>int main() {std::list<int> list1 = {1, 2, 3};std::list<int> list2 = {4, 5, 6};// 将 list2 的所有元素移动到 list1 的末尾list1.splice(list1.end(), list2);// list1: {1, 2, 3, 4, 5, 6}// list2: {}// 输出 list1std::cout << "list1: ";for (const auto& num : list1) {std::cout << num << " ";}std::cout << "\nlist2: ";for (const auto& num : list2) {std::cout << num << " ";}return 0;
}
splice 的重载形式
-
全列表移动:
void splice(iterator pos, list& other); -
移动单个元素:
void splice(iterator pos, list& other, iterator it); -
移动范围的元素:
void splice(iterator pos, list& other, iterator first, iterator last);
merge
merge 方法用于合并两个已排序的 std::list,结果仍然有序。需要确保两个列表在合并前已经排序。
#include <list>
#include <iostream>int main() {std::list<int> list1 = {1, 3, 5};std::list<int> list2 = {2, 4, 6};// 合并并排序list1.merge(list2);// list1: {1, 2, 3, 4, 5, 6}// list2: {}// 输出 list1std::cout << "Merged list1: ";for (const auto& num : list1) {std::cout << num << " ";}return 0;
}
sort
sort 方法对 std::list 中的元素进行排序。由于 std::list 是链表结构,不支持快速随机访问,内部使用归并排序算法,时间复杂度为 O(n log n)。
#include <list>
#include <iostream>int main() {std::list<int> numbers = {4, 2, 5, 1, 3};// 排序numbers.sort();// 输出排序后的列表for (const auto& num : numbers) {std::cout << num << " ";}// 输出: 1 2 3 4 5return 0;
}
reverse
reverse 方法用于反转 std::list 中的元素顺序。
#include <list>
#include <iostream>int main() {std::list<int> numbers = {1, 2, 3, 4, 5};// 反转numbers.reverse();// 输出反转后的列表for (const auto& num : numbers) {std::cout << num << " ";}// 输出: 5 4 3 2 1return 0;
}
使用场景
std::list 适用于以下场景:
- 频繁在中间插入和删除元素:如实现队列、双端队列(deque)等数据结构。
- 需要稳定的迭代器:插入和删除操作不会使其他迭代器失效。
- 不需要随机访问:主要进行顺序访问和操作。
示例:实现一个任务队列,频繁添加和移除任务。
#include <list>
#include <iostream>
#include <string>int main() {std::list<std::string> taskQueue;// 添加任务taskQueue.push_back("Task 1");taskQueue.push_back("Task 2");taskQueue.push_back("Task 3");// 插入一个高优先级任务taskQueue.push_front("High Priority Task");// 处理任务while (!taskQueue.empty()) {std::cout << "Processing: " << taskQueue.front() << std::endl;taskQueue.pop_front();}return 0;
}
示例代码
下面是一个综合示例,展示 std::list 的各种操作。
#include <iostream>
#include <list>
#include <string>int main() {// 声明和初始化std::list<std::string> fruits = {"Apple", "Banana", "Cherry"};// 插入元素fruits.push_back("Date");fruits.push_front("Apricot");auto it = fruits.begin();std::advance(it, 2); // 指向第三个元素fruits.insert(it, "Blueberry"); // 在第三个元素前插入// 输出当前列表std::cout << "Fruits list: ";for (const auto& fruit : fruits) {std::cout << fruit << " ";}std::cout << std::endl;// 输出: Fruits list: Apricot Apple Blueberry Banana Cherry Date // 删除元素fruits.remove("Banana"); // 删除所有"Banana"// 使用迭代器删除it = fruits.begin();++it; // 指向第二个元素fruits.erase(it); // 删除第二个元素// 输出修改后的列表std::cout << "After deletion: ";for (const auto& fruit : fruits) {std::cout << fruit << " ";}std::cout << std::endl;// 输出: After deletion: Apricot Blueberry Cherry Date // 排序fruits.sort();std::cout << "After sorting: ";for (const auto& fruit : fruits) {std::cout << fruit << " ";}std::cout << std::endl;// 输出: After sorting: Apricot Blueberry Cherry Date // 反转fruits.reverse();std::cout << "After reversing: ";for (const auto& fruit : fruits) {std::cout << fruit << " ";}std::cout << std::endl;// 输出: After reversing: Date Cherry Blueberry Apricot // 清空列表fruits.clear();std::cout << "After clearing, size: " << fruits.size() << std::endl;// 输出: After clearing, size: 0return 0;
}
注意事项
- 不支持随机访问:
std::list不支持通过索引访问元素。需要通过迭代器或范围for循环进行遍历。 - 内存开销:每个元素存储额外的指针(前向和后向),相比
std::vector内存开销更大。 - 缓存局部性差:由于元素不连续存储,遍历性能不如
std::vector,特别是在需要频繁遍历的场景下。 - 适用场景有限:在大多数需要顺序容器的场景下,
std::vector更高效,只有在特定情况下,如频繁的中间插入和删除,才需要考虑使用std::list。 - 操作复杂性:某些操作,如排序、合并,虽然
std::list支持,但相比std::vector的内置算法,可能需要更多的注意。
总结
std::list 是一个基于双向链表的容器,适用于需要频繁在中间位置进行插入和删除操作的场景。它提供高效的插入和删除性能,但不支持随机访问,内存开销较大,且缓存局部性差。在选择使用 std::list 之前,应权衡其优势和劣势,并考虑是否有其他更适合的容器(如 std::vector 或 std::deque)。
使用建议:
- 优先考虑使用
std::vector,因为它在大多数情况下性能更优。 - 只有在需要频繁进行中间插入和删除,且不需要随机访问时,才考虑使用
std::list。 - 考虑使用其他更现代的容器或数据结构,如
std::forward_list(单向链表),或使用std::deque进行高效的两端操作。
希望这个详细的介绍能帮助你更好地理解和使用C++中的 std::list 容器!
相关文章:
STL-list容器的使用
在C标准库中,std::list 是一个双向链表容器,提供高效的插入和删除操作,尤其适用于需要频繁在容器中间进行插入和删除元素的场景。与其他序列容器(如 std::vector 和 std::deque)相比,std::list 有其独特的优…...
java中线程与集合的面试题
在 Java 面试中,线程和集合相关的知识是非常常见的考察点。以下是几个典型的问题及答案: 线程相关面试题 什么是线程? 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个进程可以有多…...
第十五章 IRIS 进程之间的通信
文章目录 第十五章 IRIS 进程之间的通信介绍指定作业间通信设备的内存缓冲区禁用作业间通信缓冲区 作业间通信设备编号设备编号 IJC 设备的 I/O 命令OPEN命令device 设备timeout 暂停 第十五章 IRIS 进程之间的通信 本页介绍如何在两个或多个 IRIS 数据平台进程之间建立通信。…...
设计者模式之策略模式
前言 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都写在对象中,将会使对象变得异常复杂;而且有时候支持不频繁使用的算法也是一个性能负担。 如何在运行时根据需要透明地更改对象的算…...
STM32H750 COMP模拟比较器
STM32H750 COMP模拟比较器 🔖STM32H750内置两个超低功耗比较器通道(COMP1 和 COMP2). 📄功能应用: 在模拟信号的触发下从低功耗模式唤醒模拟信号调理与定时器的 PWM 输出结合使用时,构成逐周期电流控制环路…...
openresty入门教程:rewrite_by_lua_block
在OpenResty中,rewrite_by_lua_block 是一个强大的工具,它允许你在Nginx的rewrite阶段执行Lua脚本。这个阶段在Nginx处理请求的早期发生,通常用于修改请求URI、请求参数、请求头等,或者根据某些条件执行重定向、返回特定响应等。 …...
Java 并发编程学习笔记
参考资料: JAVA并发专题 - 终有救赎的专栏 - 掘金 Java并发编程学习路线(建议收藏��) | Java程序员进阶之路x沉默王二 面试题目: JUC第一讲:Java并发知识体系详解 面试题汇总(P6熟练 P7精通…...
【SpringMVC】——Cookie和Session机制
阿华代码,不是逆风,就是我疯 你们的点赞收藏是我前进最大的动力!! 希望本文内容能够帮助到你!! 目录 一:实践 1:获取URL中的参数 (1)PathVariable 2&…...
[产品管理-60]:产品的情感化设计与常用工具:感性工学、情感分析、神经网络法、微软反应卡、突发情绪法
目录 一、概述 1、情感化设计的三个层次 2、情感化设计在产品中的应用 3、情感化设计的案例 4、情感化设计的意义 二、常见工具 1、感性工学 (情商) 2、情感分析 3、神经网络法 4、微软反应卡 5、突发情绪法 一、概述 产品的情感化设计是一种…...
uniapp 小程序 周选择器
这里贴出来的是子组件的代码,父组件只是打开了一下popup // 打开了一下popup $refs.popup.open(bottom)如果不想用子组件的话,直接打开popup就可以用<template><uni-popup ref"popup" type"bottom" background-color&quo…...
Android笔记(三十二):封装一个毫秒级别倒计时View
效果 倒计时View视频 背景 业务场景需要显示带有毫秒级别的倒计时,于是自己封装一个通用的倒计时组件 源码分析 核心倒计时逻辑,主要是每隔100毫秒计算一次从开始倒计时到现在的剩余时间,并通过process接口返回出去Handler每次设置100毫秒…...
[产品管理-60]:马斯洛需求层次与产品的情感化设计
目录 一、概述 1、马斯洛需求层次理论概述 2、产品情感化设计与马斯洛需求层次的关系 3、产品情感化设计的实践案例 二、马斯洛需求层次与用户情感程度(本能、行为、反思)的关系 1、马斯洛需求层次与用户情感程度概述 2、马斯洛需求层次与用户情感…...
Python接口自动化测试自学指南(项目实战)
🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快 接口自动化测试是指通过编写程序来模拟用户的行为,对接口进行自动化测试。Python是一种流行的编程语言,它在接口自动化测试中得到了广…...
ESLint 使用教程(三):12个ESLint 配置项功能与使用方式详解
前言 在现代前端开发中,代码质量与一致性是至关重要的,ESLint 正是为此而生的一款强大工具,本文将带您详细了解 ESLint 的配置文件,并通过通俗易懂的方式讲解其主要配置项及其配置方法。此外,我们还将探讨一些高级配置…...
如何将 EDB 文件导入 Ansys HFSS 和 Ansys Q3D
EDB 文件包含有关印刷电路板 (PCB) 的基本数据,包括其布局、组件、连接性和电磁属性。将 EDB 文件导入 Ansys 工具是利用仿真和分析功能设计高效、可靠和高性能电子系统的关键步骤。在这里,我将向您展示如何将 EDB 文件导入 Ansys…...
HbuildderX运行到手机或模拟器的Android App基座识别不到设备 mac
寻找模拟器 背景: 运行的是h5,模拟器是网易MuMu。 首先检查一下是否配置dab环境,adb version 配置一下hbuilderX的adb: 将命令输出的路径配置到hbuilderx里面去,然后重启下HbuilderX。 开始安装基座…一直安装不…...
智慧流控 力行天地 | 同元软控受邀参加第十三届全国流体传动与控制学术会议
2024年10月27日-30日,由中国机械工程学会流体传动与控制分会主办的第十三届全国流体传动与控制学术会议在秦皇岛召开。大会以“智慧流控 力行天地”为主题,来自全国高校、科研院所及企事业单位的专家学者出席本次会议。 大会围绕工程应用、新型流控元件、…...
Python日志分析与故障定位
Python日志分析与故障定位 目录 📊 分布式系统日志分析:ELK Stack与Fluentd⚡ 实时日志流处理与异常检测🐍 使用Python分析并处理海量日志数据🚨 自动化故障检测与报警系统🔍 故障根因分析(Root Cause An…...
w029基于springboot的网上购物商城系统研发
🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹赠送计算机毕业设计600个选题excel文件࿰…...
Uniapp全局文件执行顺序详解
Uniapp全局文件执行顺序详解 在Uni-App项目中,全局文件的执行顺序对于深入理解应用的启动和初始化流程至关重要。本文将详细阐述这些文件的执行顺序,并提供相应的示例代码,以便开发者更好地理解和应用。 1. index.html 文件描述࿱…...
龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...
