探索C++的工具箱:双向链表容器类list(1)
引言
在C++中,std::list 是一个标准库提供的容器类,属于C++ STL(标准模板库)。std::list 是一种独特而强大的容器,它使用双向链表结构来管理元素。无论是在处理动态数据集合,还是在需要频繁进行插入和删除操作时,std::list 都展现了无与伦比的灵活性和效率。与其他线性数据结构如数组和向量相比,std::list 让开发者能够在不影响整体性能的情况下,轻松地在数据集中间添加或移除元素。
在这篇博客中,会以介绍与string类和vector类中相通的函数为主,在下一篇博客,探索C++的工具箱:双向链表容器类list(2)-CSDN博客中,会以介绍string类 和vector类中没有的、list新引入的内容为主。
1. list 简介
C++ 的 std::list 是一个双向链表(doubly linked list)的实现,与 vector 不同,list 提供 O(1) 的插入和删除操作,但不支持随机访问。使用时需要包含头文件<list>。
如果将“list”比喻为一个“购物清单”,而“list中的元素”就相当于“购物清单上的每一项商品”。
在这个比喻中:
- 整个购物清单(list)是一个容器,帮助我们整理和管理购物的内容。
- 每一项商品(list中的元素)则是清单中具体要购买的内容。
就像我们可以在购物清单上添加、删除或修改商品,列表中的元素也可以随时增删改。
std::list具有以下特点:
-
双向链表:std::list 中的每个元素都包含指向前一个元素和后一个元素的指针。这使得在链表的任意位置进行插入和删除操作非常快速(时间复杂度为O(1)),但访问随机元素的效率较低(时间复杂度为O(n))。
-
动态大小:std::list 可以根据需要动态增长或收缩,能够有效管理动态变化的数据集。
-
不连续存储:与std::vector 不同,std::list 中的元素在内存中不必连续存储,这为插入和删除操作防止内存重排提供了便利。
-
支持迭代器:std::list 支持常规和反向迭代器,可以方便地遍历链表元素。
2.list的构造
基本语法:
default (1)
explicit list (const allocator_type& alloc = allocator_type());
fill (2)
explicit list (size_type n);list (size_type n, const value_type& val,const allocator_type& alloc = allocator_type());
range (3)
template <class InputIterator>list (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());
copy (4)
list (const list& x);
list (const list& x, const allocator_type& alloc);move (5)
list (list&& x);
list (list&& x, const allocator_type& alloc);initializer list (6)
list (initializer_list<value_type> il,const allocator_type& alloc = allocator_type());
explicit 关键字用于构造函数,目的是为了防止类型转换(隐式转换)。如果一个构造函数被声明为 explicit,那么它只能通过显式的方式来调用,而不能用作隐式类型转换。
以下是构造和使用 std::list 的几种常见方法(默认使用std):
(1)默认构造函数
创建一个空的 std::list,可以使用以下代码:
list<int> myList;
(2)填充构造函数
explicit list (size_type n);list (size_type n, const value_type& val,const allocator_type& alloc = allocator_type());
上面的构造方法用来创建一个包含n个元素的list,并且元素会被初始化为默认值,在int类型中默认值是0。
下面的构造方法用来创建一个又n个元素且值会被初始化value的list
list<int> myList(5); // 创建一个包含5个元素的 list list<int> myList(5, 100); // 创建一个包含5个100的 list
(3)迭代器范围构造函数
template <class InputIterator>list (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());
#include <iostream>
#include <list>
#include <vector> int main() { vector<int> vec = {1, 2, 3, 4, 5}; list<int> myList(vec.begin(), vec.end()); // 使用范围构造函数 return 0;
}
(4)复制构造函数
list (const list& x);
list (const list& x, const allocator_type& alloc);
第二个构造函数允许自定义分配器。
#include <iostream>
#include <list> int main() { list<int> original = {1, 2, 3, 4, 5}; list<int> copiedList(original); // 使用复制构造函数return 0;
}
(5)移动构造函数
list (list&& x);
list (list&& x, const allocator_type& alloc);
同上,可以自定义分配器。移动构造函数是在 C++11 中引入的,它用于高效地构造对象,将资源(如动态分配的内存)从一个对象“转移”到另一个对象,而不是进行复制。这种方式能够提高性能,尤其是在处理管理资源的对象时(例如,标准库容器、字符串等)。
#include <iostream>
#include <list> int main() { std::list<int> original = {1, 2, 3, 4, 5}; std::list<int> movedList(std::move(original)); // 使用移动构造函数 return 0;
}
(6)初始化列表构造函数
list (initializer_list<value_type> il,const allocator_type& alloc = allocator_type());
list<int> myList = {1, 2, 3, 4, 5}; // 使用初始化列表构造函数
(7)赋值运算符重载构造函数
copy (1)
list& operator= (const list& x);move (2)
list& operator= (list&& x);initializer list (3)
list& operator= (initializer_list<value_type> il);//与初始化列表结合
#include <iostream>
#include <list> int main() { // 创建两个 list std::list<int> list1 = {1, 2, 3, 4, 5}; // 初始化 list1 std::list<int> list2; // 创建空 list2 // 使用赋值运算符将 list1 的内容复制到 list2 list2 = list1; // 执行 copy assignment return 0;
}
list容器的迭代器以及空间有关函数等与string类 和vector类的使用相似,在此不再赘述,不了解的小伙伴可以去看以下博客:
C++深入学习string类成员函数(1):默认与迭代_std::string初始化函数-CSDN博客
探索C++的存储箱:动态数组容器类vector-CSDN博客
3.list的修饰函数
3.1 emplace
`emplace` 允许在容器中直接构造对象,而不是先构造对象再进行拷贝或移动。这样可以避免不必要的性能开销。
- emplace_front(): 在链表头部直接构造元素。
- emplace_back(): 在链表尾部直接构造元素。
- emplace(): 在指定位置直接构造元素。
1. emplace_front()
在链表的开头直接构造元素:
std::list<std::string> mylist;
mylist.emplace_front("Hello");
mylist.emplace_front(5, 'A'); // 直接构造一个字符串 "AAAAA"
2. emplace_back()
在链表的末尾直接构造元素:
mylist.emplace_back("World");
mylist.emplace_back(4, 'B'); // 直接构造字符串 "BBBB"
3.emplace()
在链表的指定位置直接构造元素:
auto it = mylist.begin();
mylist.emplace(it, "Inserted"); // 在开头插入字符串 "Inserted"
emplace 与 push 系列的区别
- push_front() / push_back(): 需要先创建一个对象,然后再将其拷贝或移动到容器中。
- emplace_front() / emplace_back(): 直接在容器中的位置构造对象,省去了拷贝或移动的过程,适合构造复杂对象时提高效率。
使用场景
当你需要直接在容器中构造元素,并且想避免额外的临时对象构造和销毁时,`emplace` 系列函数非常有用,尤其是在需要构造复杂对象或容器时,性能优势明显。
3.2 assign
`std::list` 的 `assign()` 函数用于替换链表中的元素。它提供了三种重载方式来支持不同的输入类型。以下是每种 `assign()` 函数的具体说明:
range (1)
template <class InputIterator>void assign (InputIterator first, InputIterator last);fill (2)
void assign (size_type n, const value_type& val);initializer list (3)
void assign (initializer_list<value_type> il);
1.范围构造函数
- 功能:将链表中的元素替换为 `[first, last)` 范围内的元素。此函数使用两个迭代器来指定范围,可以从另一个容器或范围中拷贝元素。
std::list<int> mylist;std::vector<int> vec = {1, 2, 3, 4, 5};mylist.assign(vec.begin(), vec.end()); // 复制 vector 中的所有元素到 list
2.填充构造函数
- 功能:将链表中的元素替换为 `n` 个值为 `val` 的元素。此函数将链表填充为 `n` 个相同的元素。
std::list<int> mylist;mylist.assign(5, 100); // 用 5 个值为 100 的元素填充 list
3.初始化列表构造函数
- 功能:将链表中的元素替换为初始化列表 `il` 中的元素。此函数允许使用 C++11 引入的初始化列表语法进行赋值。
std::list<int> mylist;mylist.assign({1, 2, 3, 4, 5}); // 通过初始化列表填充 list
4.assign() 函数的使用场景
- 范围赋值(Range Assign):适用于从另一个容器或范围内复制数据,例如从 `vector` 或 `array` 中将某个范围的元素赋值到 `list` 中。
- 填充赋值(Fill Assign):适合需要将链表中的所有元素设置为相同的值时使用,例如初始化某个固定大小的列表。
- 初始化列表赋值(Initializer List Assign):简化了使用固定值集合初始化 `list` 的过程,尤其是在初始化时可以快速赋值。
3.3 push与pop
在 C++ std::list 中,push 和 pop,分别用于向链表中添加元素和移除元素。它们有两种常用的形式:操作链表头部和尾部的元素。
(1)push 系列
1. push_front()
- 功能:在链表头部插入一个元素。
std::list<int> mylist;mylist.push_front(10); // 在链表头部插入 10mylist.push_front(20); // 在链表头部插入 20,链表现在是 {20, 10}
2. push_back()
- 功能:在链表尾部插入一个元素。
std::list<int> mylist;mylist.push_back(10); // 在链表尾部插入 10mylist.push_back(20); // 在链表尾部插入 20,链表现在是 {10, 20}
(2)pop 系列
1. pop_front()
- 功能:移除链表头部的元素。
- 注意:此操作不返回被移除的元素。如果链表为空,调用此函数会导致未定义行为。
std::list<int> mylist = {10, 20, 30};mylist.pop_front(); // 移除头部的 10,链表现在是 {20, 30}
2. pop_back()
- 功能:移除链表尾部的元素。
- 注意:与 `pop_front()` 一样,此操作不返回被移除的元素,链表为空时调用会导致未定义行为。
std::list<int> mylist = {10, 20, 30};mylist.pop_back(); // 移除尾部的 30,链表现在是 {10, 20}
3.4 其它函数
- insert():在链表的指定位置插入元素或范围内的多个元素。
- swap():交换两个链表的内容。
- erase():删除链表中的某个元素或一段范围内的元素。
- resize():调整链表大小,可能会插入或删除元素。
- clear():清空链表,删除所有元素。
1. insert()
- 功能:在指定位置插入一个或多个元素。
- 重载形式:
single element (1)
iterator insert (const_iterator position, const value_type& val);
fill (2)
iterator insert (const_iterator position, size_type n, const value_type& val);
range (3)
template <class InputIterator>
iterator insert (const_iterator position, InputIterator first, InputIterator last);
1. 插入单个元素:
在 `position` 迭代器处插入 `val`,返回指向新插入元素的迭代器。
2. 插入多个相同值的元素:
在 `position` 处插入 `n` 个 `val`。
3. 插入范围内的元素:
将 `[first, last)` 范围内的元素插入到 `position` 处。
2. swap()
- 功能:交换两个 `list` 容器的内容。
std::list<int> list1 = {1, 2, 3};std::list<int> list2 = {4, 5, 6};list1.swap(list2); // list1 变为 {4, 5, 6},list2 变为 {1, 2, 3}
3. erase()
- 功能:删除指定位置的元素或指定范围内的元素。
- 重载形式:
iterator erase (const_iterator position);
iterator erase (const_iterator first, const_iterator last);
1. 删除单个元素:
删除 `position` 处的元素,返回指向被删除元素后面的元素迭代器。
2. 删除范围内的元素:
删除 `[first, last)` 范围内的所有元素,返回指向删除范围后第一个元素的迭代器。
4. resize()
- 功能:调整链表的大小。如果新大小比当前小,删除多余的元素;如果新大小比当前大,则插入默认值的元素或指定值的元素。
- 用法:
void resize (size_type n);
void resize (size_type n, const value_type& val);
1. 仅调整大小:
调整链表大小为 `n`,如果新大小比当前大,则插入默认值的元素。
2. 调整大小并指定新元素的值:
调整链表大小为 `n`,并用 `val` 填充新插入的元素。
std::list<int> mylist = {1, 2, 3};mylist.resize(5, 100); // 调整大小为 5,列表变为 {1, 2, 3, 100, 100}mylist.resize(2); // 缩小大小为 2,列表变为 {1, 2}
5. clear()
- 功能:清空链表,删除所有元素,链表大小变为 0,但不改变链表的容量。
std::list<int> mylist = {1, 2, 3, 4};mylist.clear(); // 清空列表,列表现在为空
通过本篇文章,我们详细了解了 C++ `list` 容器的基本操作与应用场景。`list` 提供了双向链表的灵活性,能够高效地进行插入、删除等操作,尤其在需要频繁修改容器中的元素时表现优异。然而,正如我们所讨论的,它在随机访问性能上不如其他顺序容器(如 `vector`),因此在选择容器时,应根据实际需求权衡各个方面。
本篇博客就到此为止了,与vector、string容器不同、list新引入的内容都在探索C++的工具箱:双向链表容器类list(2)-CSDN博客中,有兴趣的小伙伴可以跳转学习
相关文章:
探索C++的工具箱:双向链表容器类list(1)
引言 在C中,std::list 是一个标准库提供的容器类,属于C STL(标准模板库)。std::list 是一种独特而强大的容器,它使用双向链表结构来管理元素。无论是在处理动态数据集合,还是在需要频繁进行插入和删除操作时…...
大厂高频算法考点--单调栈
什么是单调栈: 单调栈就是借助一个栈,在仅仅使用当前栈的条件下,时间复杂度是N(n),将每个节点最有离这他最近的大于或者是小于的数据返回,将已知数组的元素放到栈里。再自我实现的代码里面我们使用数组实现…...
Unity使用Git及GitHub进行项目管理
git: 工作区,暂存区(存放临时要存放的内容),代码仓库区1.初始化 git init 此时展开隐藏项目,会出现.git文件夹 2.减小项目体积 touch .gitignore命令 创建.gitignore文件夹 gitignore文件夹的内容 gitignore中添加一下内容 # This .gitignore file should be place…...
如何将本地 Node.js 服务部署到宝塔面板:完整的部署指南
文章简介: 将本地开发的 Node.js 项目部署到线上服务器是开发者常见的工作流程之一。在这篇文章中,我将详细介绍如何将本地的 Node.js 服务通过宝塔面板(BT 面板)上线。宝塔面板是一个强大的服务器管理工具,具有简洁的…...
SpringBoot项目启动报错:命令行太长解决
文章目录 SpringBoot项目启动报错:命令行太长解决1. 第一种方法1. 第二种方法1-1 旧版本Idea1-2 新版本Idea 3. 重新启动SpringBoot项目即可解决 SpringBoot项目启动报错:命令行太长解决 报错信息: 1. 第一种方法 1. 第二种方法 找到项目…...
使用Docker启动的Redis容器使用的配置文件路径等问题以及Python使用clickhouse_driver操作clickhouse数据库
一、使用Docker启动的Redis容器使用的配置文件路径等问题 1.docker启动的redis使用的配置文件路径是什么 使用docker搭建redis服务,本身redis启动的时候可以指定配置文件的, redis-server /指定配置文件路径/redis.conf。 但手上也没有一个redis配置文件…...
硬盘格式化后能恢复数据吗?4款好用的数据恢复软件,格式化后也能安心
咱们今天来谈谈一个挺烦人的问题——硬盘格式化后能恢复数据吗?别担心,能的!只要你用对方法,就算硬盘被清空了,那些重要文件还是能找回来的。下面,我就给你们介绍几款超给力的数据恢复软件,让你…...
【选择C++游戏开发技术】
在选择C游戏开发技术时,以下几个因素是需要考虑的: 1. 游戏类型:不同类型的游戏可能需要不同的技术。例如,2D游戏通常采用基于精灵的引擎,而3D游戏通常采用基于物理模拟的引擎。根据游戏类型选择适合的技术是很重要的…...
Oracle数据库系统表空间过大,清理SYSTEM、SYSAUX表空间
一.前言 在oracle数据库中,system为系统表空间,存放着一些我们经常用到的系统表和视图,sysaux为辅助表空间,辅助着系统表空间。这两个表空间不宜添加数据文件,会使系统表空间过于臃肿,从而影响数据库的使用…...
LaTeX参考文献工具和宏包bibmap项目简介
LaTeX参考文献工具和宏包bibmap项目简介 LaTeX 中的参考文献生成方式主要有三种:第一种是手动写thebibliography环境的,第二种事基于bibtex程序的,第三种则是基于biblatex宏包和biber程序的。本文介绍的bibmap项目则提供了第四种方法。目前b…...
微软的 Drasi:一种轻量级的事件驱动编程方法
微软的开源数据变化处理平台有望提供一种全新的方式来构建和管理可产生持续事件流的云应用程序。 Microsoft Azure 孵化团队是微软超大规模云中比较有趣的组成部分之一。它介于传统软件开发团队和研究组织之间,致力于构建大规模分布式系统问题的解决方案。 这些解决…...
vue3 笔记-插槽
结构类似的模块,我们可以考虑用插槽,以便后续复用: 代码: 1.插槽 <script setup> defineProps({title: {required: true,type: String},number: {required: true,type: Number} }) </script><template><d…...
C# 字符串常用方法
文章目录 Length:获取字符串中字符的个数(不包括末尾的空字符)ToLower() 和 ToUpper():将字符串转换为小写或大写形式Substring(int startIndex, int length):从指定索引开始截取指定长度的子字符串Remove(int startIn…...
字节跳动青训营——入营考核解答(持续更新中~~~)
考核内容: 在指定的题库中自主选择不少于 15 道算法题并完成解题,其中题目难度分配如下: 简单题不少于 10 道中等题不少于 4 道困难题不少于 1 道 解答代码 8.进制求和转换(难) 代码实现: import jav…...
JavaWeb合集15-Apache POI
十五、Apache POI Apache POI是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是,我们可以使用POI在Java 序中对Miscrosoft Office各种文件进行读写操作。一般情况下,POI都是用于操作Excel文件。 使用场景:银行网银系统导出交…...
Threejs 实现3D 地图(01)创建基本场景
"d3": "^7.9.0", "three": "^0.169.0", "vue": "^3.5.10" <script setup> import { onMounted,ref } from vue import * as THREE from three import * as d3 from "d3"; //莫开托坐标 矫正地图…...
snmpdelta使用说明
1.snmpdelta介绍 snmpdelta命令是用来获取下一个节点的OID的值。 2.snmpdelta安装 1.snmpdelta安装 命令: yum -y install net-snmp net-snmp-utils [root@logstash ~]# yum -y install net-snmp net-snmp-utils Loaded plugins: fastestmirror Loading mirror speeds f…...
Hadoop集群安装
集群规划 node01node02node03角色主节点从节点从节点NameNode√DataNode√√√ResourceManager√NodeManager√√√SecondaryNameNode√Historyserver√ 上传安装包到node01 解压到指定目录 tar -zxvf /bigdata/soft/hadoop-3.3.3.tar.gz -C /bigdata/server/ 创建软链接 cd…...
VuePress集成到Vue项目的方法
VuePress 可以作为一个独立的静态站点生成器来使用,也可以集成到现有的 Vue 项目中。以下是将 VuePress 集成到 Vue 项目的几种方法: 1. 作为本地依赖集成 如果你想在现有的 Vue 项目中使用 VuePress 来管理文档,你可以将 VuePress 安装为本…...
【ROS】ROS局域网下多机通讯方法
最近工作中需要用到多机通讯,这里稍微总结一下使用方法。 目录 一、网络配置 二、修改两个设备的hosts文件 三、修改两个ros设备的.bashrc 四、launch文件中给节点设定运行的设备 一、网络配置 首先确保两个ros设备连接到同一局域网下,然后查询两个…...
【PythonAI】2.2.2 技能实训:使用Pandas读取CSV/Excel文件,查看数据概览(2. 数据质量评估)
import pandas as pd import numpy as np# 设置显示选项(统信UOS终端适配) pd.set_option(display.max_columns, None) pd.set_option(display.width, 1000) pd.set_option(display.max_colwidth, 50)# 读取CSV文件 df pd.read_csv(dirty_reviews.csv)#…...
嵌入式Linux新手必看:Buildroot根文件系统启动后权限问题全解析(附/dev/console修复指南)
嵌入式Linux权限管理实战:Buildroot根文件系统权限问题深度解析与修复指南 当你在嵌入式Linux开发中首次使用Buildroot构建系统时,可能会遇到一个令人头疼的问题——系统启动后没有root权限,甚至无法访问/dev/console设备。这不仅影响系统功能…...
2023长城杯Web赛题解析:从SSRF到Pickle反序列化的实战攻防
1. 从SSRF漏洞到内网渗透的实战突破 去年参加长城杯时遇到一道名为"seeking"的Web题目,让我对SSRF漏洞的利用有了全新认识。题目一开始给出了一个看似简单的PHP文件,但隐藏着精妙的设计。代码中通过file_get_contents函数获取图片内容时&#…...
避坑指南:Python调用摄像头常见问题(驱动、权限、多摄像头切换)与解决方案
Python摄像头开发避坑实战:从驱动调试到多设备管理的完整解决方案 当你兴奋地写完了Python摄像头调用代码,按下运行键时,屏幕上却跳出"无法打开视频设备"的错误提示——这种挫败感我太熟悉了。作为经历过无数次摄像头调试折磨的开发…...
Windows系统Btrfs文件系统实用指南
Windows系统Btrfs文件系统实用指南 【免费下载链接】btrfs WinBtrfs - an open-source btrfs driver for Windows 项目地址: https://gitcode.com/gh_mirrors/bt/btrfs 在数字化存储需求日益增长的今天,文件系统的选择直接影响数据安全性与存储效率。WinBtrf…...
终极图像分类指南:从海豚到多类别的机器学习实战
终极图像分类指南:从海豚到多类别的机器学习实战 【免费下载链接】have-fun-with-machine-learning An absolute beginners guide to Machine Learning and Image Classification with Neural Networks 项目地址: https://gitcode.com/gh_mirrors/ha/have-fun-wit…...
5分钟搞定HeyGem数字人视频生成:科哥二次开发版,批量处理指南
5分钟搞定HeyGem数字人视频生成:科哥二次开发版,批量处理指南 1. 系统简介与核心价值 HeyGem数字人视频生成系统批量版是科哥基于原版进行的二次开发版本,专门针对企业级批量视频生成需求进行了优化。这个工具能够将一段音频与多个视频素材…...
第7章 运算符-7.6 成员运算符
成员运算符用于检查字符串、列表、元组、字典和集合中是否存在指定的元素。表7-6中列出了Python中的成员运算符,在该表中,假设变量a的值为3,变量lt的值为[1,2,3,4]。表7-6 成员运算符运算符描述实例in如果在字符串、列表、元组、字典和集合中…...
探索MacOS窗口管理新境界:3步掌握Easy Move+Resize高效操作
探索MacOS窗口管理新境界:3步掌握Easy MoveResize高效操作 【免费下载链接】easy-move-resize Adds "modifier key mouse drag" move and resize to OSX 项目地址: https://gitcode.com/gh_mirrors/ea/easy-move-resize Easy MoveResize是一款专为…...
从实战案例出发:面阵与线阵相机选型策略及镜头配置全解析
1. 面阵与线阵相机的本质区别 第一次接触工业相机选型时,我也曾被各种参数搞得晕头转向。直到有次在产线上亲眼看到两种相机的实际表现,才真正理解了它们的差异。简单来说,面阵相机就像我们平时用的数码相机,一次拍摄就能获取整个…...
