探索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设备连接到同一局域网下,然后查询两个…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
Visual Studio Code 扩展
Visual Studio Code 扩展 change-case 大小写转换EmmyLua for VSCode 调试插件Bookmarks 书签 change-case 大小写转换 https://marketplace.visualstudio.com/items?itemNamewmaurer.change-case 选中单词后,命令 changeCase.commands 可预览转换效果 EmmyLua…...
论文阅读:Matting by Generation
今天介绍一篇关于 matting 抠图的文章,抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法,已经有很多的工作和这个任务相关。这两年 diffusion 模型很火,大家又开始用 diffusion 模型做各种 CV 任务了&am…...
二维FDTD算法仿真
二维FDTD算法仿真,并带完全匹配层,输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...
客户案例 | 短视频点播企业海外视频加速与成本优化:MediaPackage+Cloudfront 技术重构实践
01技术背景与业务挑战 某短视频点播企业深耕国内用户市场,但其后台应用系统部署于东南亚印尼 IDC 机房。 随着业务规模扩大,传统架构已较难满足当前企业发展的需求,企业面临着三重挑战: ① 业务:国内用户访问海外服…...
