C++(STL)的List解读

news2024/9/20 22:33:15

目录

list简介

list的几个特性

接口函数

1.默认成员函数

2.迭代器相关函数

3.容量相关的函数

4.成员访问相关的函数

5.modify系列

6.operation系列

7.重载在全局的函数


list简介

Lists are sequence containers that allow constant time insert and erase operations anywhere within the sequence, and iteration in both directions.

在C语言中,我们已经学过了一些基础的带哨兵位的双向链表,但是链表的实现比较“呆板”,因此C++中就出现了list。list是一个模板类,功能就类似双向链表。

list的几个特性

1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向
其前一个元素和后一个元素。
3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高
效。
4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率
更好。
5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list
的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间
开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这
可能是一个重要的因素)
下面我们就从list的几个接口函数来探索这些特点。

接口函数

1.默认成员函数

构造函数

构造函数主要是由三种构成:1)n个val      2)默认构造函数(全缺省)      3)用迭代器传参

	list<int> lt(10, 0);
	list<int> lt2;
	list<int> lt3(lt.begin(), lt.end());

拷贝构造

用于构造一个和原对象一样的“个体”

	list<int> lt(10, 0);
	list<int> lt2(lt);

赋值重载

起到赋值的作用

int main()
{
	list<int> lt(10, 0);
	list<int> lt2(10, 1);
	list<int> lt3(lt);

	lt3 = lt2;

	for (auto& ch : lt3)
	{
		cout << ch << " ";
	}

	cout << endl;
	return 0;
}

2.迭代器相关函数

主要是begin()、end()、rbegin()、rend()

迭代器的行为与指针相似,但不能说迭代器就等价于指针。迭代器可以用来遍历容器。

int main()
{
	list<int> lt(10, 0);
	list<int> lt2(10, 1);
	list<int> lt3(lt);

	lt3 = lt2;
	auto it = lt3.begin();
	
	while (it != lt3.end())
	{
		cout << *it << " ";
		++it;
	}

	cout << endl;
	return 0;
}

list的迭代器是双向迭代器,支持++、--、*(解引用)操作

rbegin和rend则是反向迭代器,可以将数据反向遍历。cbegin系列则是const修饰的迭代器。

但是cbegin系列实际上begin系列已经完成了const迭代器的重载

3.容量相关的函数

这部分函数主要是与容量相关的。但是不同于string和vector,他不含有reserve、capacity函数。因为list是由一个个节点构成,所以不存在扩容的概念。当需要额外的空间时,只需要额外获取节点即可。

1)empty

用来检测是否是空container

2)size

用来监视容量

3)max_size

用来检测能允许的最大空间。但是实践中意义不大,毕竟也没有工程会傻到用完所有空间。

4.成员访问相关的函数

front()函数返回第一个有效元素。

back()返回最后一个有效元素。

5.modify系列

这个系列主要是与增删查改有关。

1)assign函数

清除原先的空间、内容,并且重新分配内容,并给予适配的空间大小。

可以使用迭代器传参,也可以使用n个val传参。

2)头插、头删、尾插、尾删系列

主要是应用push_back 和 pop_back组合、push_front和pop_front组合

在插入时,只需要给出对应的数据。在删除时不需要传参。

3)insert

insert可以支持在任意位置插入数据,因此在传入参数时就需要传入迭代器用来指明对应的位置。

传入迭代器的位置之后,可以传入val、n个val和迭代器区间。

#include <iostream>
#include <list>

int main() {
    std::list<int> my_list = {1, 2, 3, 4};
    auto it = my_list.begin(); // 获取开始迭代器
    ++it; // 移动迭代器到第二个元素
    my_list.insert(it, 'a'); // 在第二个元素之前插入'a'
    for (const auto& elem : my_list) {
        std::cout << elem << ' ';
    }
    std::cout << std::endl;
    return 0;
}
// 输出: 1 a 2 3 4

#include <iostream>
#include <list>

int main() {
    std::list<int> my_list = {1, 2, 3, 4};
    auto it = my_list.end(); // 获取结束迭代器
    my_list.insert(it, 3, 'b'); // 在列表末尾插入3个'b'
    for (const auto& elem : my_list) {
        std::cout << elem << ' ';
    }
    std::cout << std::endl;
    return 0;
}
// 输出: 1 2 3 4 b b b

#include <iostream>
#include <list>

int main() {
    std::list<int> my_list = {1, 2, 3, 4};
    std::list<int> to_insert = {5, 6, 7};
    auto it = my_list.begin(); // 获取开始迭代器
    ++it; // 移动迭代器到第二个元素
    my_list.insert(it, to_insert.begin(), to_insert.end()); // 在第二个元素之前插入另一个列表的所有元素
    for (const auto& elem : my_list) {
        std::cout << elem << ' ';
    }
    std::cout << std::endl;
    return 0;
}
// 输出: 1 5 6 7 2 3 4

4)erase函数

用来删除数据。可以删除某个数据,也可以删除某块区间的数据。

返回删除数据的下一个迭代器。

5)swap函数

用来交换两个list的空间和内容。

#include <iostream>
#include <list>

int main() {
    std::list<int> list1 = {1, 2, 3, 4};
    std::list<int> list2 = {5, 6, 7, 8};

    // 输出交换前的列表
    std::cout << "Before swap:" << std::endl;
    std::cout << "list1: ";
    for (const auto& elem : list1) {
        std::cout << elem << ' ';
    }
    std::cout << std::endl;

    std::cout << "list2: ";
    for (const auto& elem : list2) {
        std::cout << elem << ' ';
    }
    std::cout << std::endl;

    // 交换两个列表的内容和空间
    list1.swap(list2);

    // 输出交换后的列表
    std::cout << "After swap:" << std::endl;
    std::cout << "list1: ";
    for (const auto& elem : list1) {
        std::cout << elem << ' ';
    }
    std::cout << std::endl;

    std::cout << "list2: ";
    for (const auto& elem : list2) {
        std::cout << elem << ' ';
    }
    std::cout << std::endl;

    return 0;
}

执行结果: 

Before swap:
list1: 1 2 3 4 
list2: 5 6 7 8 
After swap:
list1: 5 6 7 8 
list2: 1 2 3 4 

6)resize

用来调整size的大小。

当n<size时,会减小size到n;

当n>size时,会增大到n,并且将后续的内容初始化为val。

7)clear

用来清除空间和内容。list是带哨兵位的双向链表,clear()函数并不会清除掉哨兵位。

6.operation系列

1)splice函数

这是splice函数的介绍。介绍中写道将一个list中的元素转移到另一个链表中。注意:该转移不是单纯数据的转移,而是体现在节点的转移。

可以挪动整个list,可以挪动list的某个元素,也可以挪动某一段区间。

Transfers elements from x into the container, inserting them at position.

1.单元素移动

将单个元素从另一个 list 移动到当前 list 的指定位置之前。

#include <iostream>
#include <list>

int main() {
    std::list<int> list1 = {1, 2, 3, 4};
    std::list<int> list2 = {5, 6, 7, 8};

    // 将 list2 中的第一个元素(5)移动到 list1 的第三个位置之前
    auto it = list1.begin(); // 获取 list1 的开始迭代器
    std::advance(it, 2); // 将迭代器向前移动两位,指向第三个位置
    list1.splice(it, list2, list2.begin()); // 移动操作

    // 输出结果
    for (int elem : list1) std::cout << elem << ' ';
    std::cout << std::endl;
    for (int elem : list2) std::cout << elem << ' ';
    std::cout << std::endl;

    return 0;
}

list2.begin() 是源迭代器,在 splice 操作后它将失效,因为它指向的元素已经被移动到 list1 中。it 是目标迭代器,它在操作后仍然有效,并且指向移动后的元素 3。

输出结果:

1 2 5 3 4 
6 7 8 
 

2)范围移动

将另一个 list 中的一个范围的所有元素移动到当前 list 的指定位置。

#include <iostream>
#include <list>

int main() {
    std::list<int> list1 = {1, 2, 3, 4};
    std::list<int> list2 = {5, 6, 7, 8};

    // 将 list2 中的前两个元素移动到 list1 的末尾
    list1.splice(list1.end(), list2, list2.begin(), ++std::list<int>::iterator(list2.begin()));

    // 输出结果
    for (int elem : list1) std::cout << elem << ' ';
    std::cout << std::endl;
    for (int elem : list2) std::cout << elem << ' ';
    std::cout << std::endl;

    return 0;
}

代码中,迭代器iterator是一个对象,因此采用了匿名对象传参的方式。

输出结果:

1 2 3 4 5 6 
7 8 
 

3)整个容器移动

#include <iostream>
#include <list>

int main() {
    std::list<int> list1 = {1, 2, 3, 4};
    std::list<int> list2 = {5, 6, 7, 8};

    // 将整个 list2 移动到 list1 的末尾
    list1.splice(list1.end(), list2);

    // 输出结果
    for (int elem : list1) std::cout << elem << ' ';
    std::cout << std::endl;
    // list2 现在为空
    for (int elem : list2) std::cout << elem << ' ';
    std::cout << std::endl;

    return 0;
}

在这个例子中,list2 的所有迭代器在 splice 操作后都将失效,因为整个 list2 的内容都被移动到了 list1 中。目标迭代器 list1.end() 在操作后仍然有效,并且指向新插入元素之后的元素。

输出:

1 2 3 4 5 6 7 8 
 

总结:

在 splice 操作后,如果是从源 list 移动单个元素或一个范围,则源 list 中被移动元素之前的迭代器仍然有效,而被移动元素及其之后的迭代器失效目标 list 的迭代器在操作后仍然有效。如果整个源 list 被移动,则源 list 的所有迭代器失效

2)remove函数

用来移除特定的元素

 

示例代码

#include <iostream>
#include <list>

int main() {
    // 创建一个list
    std::list<int> lst = {1, 2, 3, 4, 2, 5, 2};

    // 打印原始list
    std::cout << "原始list: ";
    for (int i : lst) {
        std::cout << i << " ";
    }
    std::cout << std::endl;

    // 移除所有值为2的元素
    lst.remove(2);

    // 打印修改后的list
    std::cout << "修改后的list: ";
    for (int i : lst) {
        std::cout << i << " ";
    }
    std::cout << std::endl;

    return 0;
}

原始list: 1 2 3 4 2 5 2
修改后的list: 1 3 4 5
 

使用remove时,不需要进行sort(排序)

3)sort函数

用来完成排序工作。默认排序是升序

#include <iostream>
#include <list>
#include <algorithm> // 用于sort函数

int main() {
    // 创建一个list
    std::list<int> lst = {4, 1, 3, 5, 2};

    // 打印原始list
    std::cout << "原始list: ";
    for (int i : lst) {
        std::cout << i << " ";
    }
    std::cout << std::endl;

    // 使用sort函数对list进行排序
    lst.sort();

    // 打印排序后的list
    std::cout << "排序后的list: ";
    for (int i : lst) {
        std::cout << i << " ";
    }
    std::cout << std::endl;

    return 0;
}

输出:

原始list: 4 1 3 5 2 
排序后的list: 1 2 3 4 5 
 

需要注意的是,sort在list有内置函数接口,在算法库中也有函数接口。

list内置:

算法库:

对于list对象,默认的迭代器是bidirectional迭代器(双向迭代器)

对于算法库中的sort,需要传入random迭代器(随机迭代器)

就迭代器而言,在功能上有const迭代器、非const迭代器、正向、反向迭代器;在性质上有随机迭代器、双向迭代器、单向迭代器。

就迭代器的权限而言:随机>双向>单项

随机支持:++ / -- / + / - 操作                (vector、string 、deque)

双向支持:++  / --                                       (list 、红黑树(map和set))

单向支持: ++                                         (单链表、哈希表)

因此随机迭代器可以用算法库(algorithm)函数中的sort,也可以用list中的sort

但是双向迭代器没法将权限上升到算法库中的sort,只能使用bidirectional专属的sort

sort内部也是消耗比较大的接口函数,因此sort函数尽量少用。

4)unique函数

用来完成去重工作。但是也有前提,即list必须是有序的。

示例代码:

#include <iostream>
#include <list>

int main() {
    // 创建一个list
    std::list<int> lst = {1, 2, 2, 3, 4, 4, 4, 5, 5, 6};

    // 打印原始list
    std::cout << "原始list: ";
    for (int i : lst) {
        std::cout << i << " ";
    }
    std::cout << std::endl;

    // 使用unique函数移除连续的重复元素
    lst.unique();

    // 打印修改后的list
    std::cout << "修改后的list: ";
    for (int i : lst) {
        std::cout << i << " ";
    }
    std::cout << std::endl;

    return 0;
}

输出:

原始list: 1 2 2 3 4 4 4 5 5 6 
修改后的list: 1 2 3 4 5 6 
 

5)merge函数

merge是合并的意思,用来合并两个list对象

在C++标准模板库(STL)中,list容器的merge成员函数用于将两个已排序的list合并成一个新的已排序的list。这个函数特别适用于list,因为它可以高效地执行合并操作,而不需要额外的存储空间,因为它直接在现有的节点之间重新链接

以下是对merge函数的几个关键点的说明:

  1. 两个list都必须已排序:在调用merge之前,两个list必须根据相同的排序准则进行排序。

  2. 合并操作merge函数会将第二个list(参数x)中的所有元素合并到调用mergelist中。合并后的list仍然保持已排序状态。

  3. 效率:由于list的双向链表特性,合并操作是非常高效的。它不需要像数组或vector那样进行大量的元素移动。

  4. 第二个list的状态:在合并操作后,参数x指定的list变为空。

  5. 比较函数:如果不提供比较函数,则使用默认的operator<来比较元素。

示例:

#include <iostream>
#include <list>

int main() {
    std::list<int> lst1 = {1, 3, 5, 7};
    std::list<int> lst2 = {2, 4, 6, 8};

    // 确保两个list都已排序
    lst1.sort();
    lst2.sort();

    // 将lst2合并到lst1
    lst1.merge(lst2);

    // lst2现在应该是空的
    std::cout << "lst2的大小: " << lst2.size() << std::endl;

    // 打印合并后的lst1
    std::cout << "合并后的lst1: ";
    for (int i : lst1) {
        std::cout << i << " ";
    }
    std::cout << std::endl;

    return 0;
}

在这个例子中,我们首先创建了两个已排序的listlst1lst2。然后我们使用merge函数将lst2合并到lst1中。合并后,lst2变为空,而lst1包含了两个原始list的所有元素,并且仍然保持排序顺序。        

输出:

lst2的大小: 0
合并后的lst1: 1 2 3 4 5 6 7 8 
 

如果是乱序,采用merge函数之后,导致合并后的list也是乱序的。

std::list<int> lst1 = {5, 3, 1, 7};
std::list<int> lst2 = {8, 6, 4, 2};
如果我们直接合并这两个list:合并后的lst1将包含以下元素,且顺序是它们在原始list中的顺序:

输出:

5, 3, 1, 7, 8, 6, 4, 2

为了得到一个排序后的list,你需要在调用merge函数之前分别对两个list进行排序

6)reverse

用来逆置list

7.重载在全局的函数

第一部分是关系运算符的重载

第二部分是swap函数

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

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

相关文章

【Linux】阻塞信号|信号原理|深入理解捕获信号|内核态|用户态|sigaction|可重入函数|volatile|SIGCHILD|万字详解

目录 ​编辑 一&#xff0c;常见的信号术语 二&#xff0c;信号在内核中的表示 信号标志位 Pending表 Block表 handler表 POSIX.1标准 三&#xff0c;sigset_t 信号集操作函数 sigemptyset sigfillset sigaddset sigdelset sigismember sigprocmask sig…

ISP 代理与住宅代理 – 终极指南

模拟自然的、类似人类的流量可能很麻烦&#xff0c;但对于某些任务&#xff08;如帐户管理或网络自动化&#xff09;&#xff0c;没有它就无法完成。ISP 和住宅代理都可以提供帮助&#xff0c;但您不能盲目购买和部署它们。您需要了解它们的优势&#xff0c;理解它们的弱点&…

买的谷歌游戏账号被删了?如果是企业账号找回来的可能性很小。一个识别谷歌企业号的简单方法

这段时间有几个朋友找到我说&#xff0c;自己买的谷歌账号登录不了&#xff0c;有的是直接提示被删除&#xff0c;问能否恢复。 我了解了一下&#xff0c;发现他们的账号都是购买的企业账号。这种企业账号一旦被管理员删除了是无法恢复的。 特此记录下来&#xff0c;提醒各位…

8月13日笔记

msf补充 使用方法&#xff1a; 进入框架&#xff1a;msfconsole 使用search命令查找相关漏洞&#xff1a;search ms14-058 使用info查看模块信息&#xff1a;info 我们也可以将攻击代码写configure.rc&#xff08;只要是以 .rc 结尾的文件&#xff09;配置文件中&#xff0c;然…

【数学建模】介绍论文书写格式

&#x1f308;个人主页&#xff1a;Yui_ &#x1f308;Linux专栏&#xff1a;Linux &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;数据结构专栏&#xff1a;数据结构 文章目录 1.论文整体排版2. 标题书写3.摘要书写4.参考文献5.公式编辑5.1 常用公式编辑方法…

JavaScript 逆向技巧总结

本节属于知识总结&#xff0c;只是对思路的梳理&#xff0c;不对具体内容进行展开 JS 逆向可以分为三大部分&#xff1a; 寻找入口&#xff0c; 调试分析&#xff0c; 模拟执行 寻找入口&#xff1a; 这是非常关键的一步&#xff0c;逆向在大部分情况下就是找一些加密参数到底…

Linux磁盘管理与文件系统(二):实用工具和命令、fdisk分区示例

文章目录 4、查看或管理磁盘分区-fdisk格式选项示例 4、示例&#xff1a;使用 fdisk 命令创建分区需求操作步骤 5、创建文件系统-mkfs格式常用选项示例创建其他类型的文件系统 6、创建文件系统-mkswap格式常用选项示例拓展&#xff1a;关闭和启用交换分区拓展&#xff1a;swap分…

搬瓦工澳大利亚AS9929 VPS测评

搬瓦工澳大利亚vps怎么样&#xff1f;搬瓦工澳大利亚悉尼数据中心在运作CUII/AS9929线路的VPS&#xff0c;底层为KVM虚拟&#xff0c;纯SSD阵列&#xff0c;1Gbps带宽... 目前看到的是CPU主频是2.4GHz&#xff0c;接入XTOM网络&#xff0c;IP归属澳大利亚&#xff0c;当前大致I…

window好用的批量远程桌面连接工具

下载 安装 Remote Desktop Connection Manager 添加server

AlexNet模型搭建(三部曲_2)

文章目录 1模型介绍2 模型搭建3 模型训练4 模型预测 猫狗二分类&#xff0c;模型简单&#xff0c;训练精度并不高。数据集下载&#xff1a;<https://aistudio.baidu.com/datasetdetail/26884> 百度飞浆上找的大小只有60多M 1模型介绍 AlexNet是一个卷积神经网络的名字&a…

Linux命令(基础面试可用,都是自己觉得平时使用多的)

1.cat 参数&#xff1a;-n&#xff1a;显示行号-s&#xff1a;压缩连续的空行&#xff0c;只显示一个空行2.chattr 改变文件属性 语法&#xff1a;chattr [-RV] [/-/<属性>][文件或目录] 属性&#xff1a;a&#xff1a;让文件或目录仅供附加用途i&#xff1a;不得任意更…

MediaPipe人体姿态、手指关键点检测

MediaPipe人体姿态、手指关键点检测 文章目录 MediaPipe人体姿态、手指关键点检测前言一、手指关键点检测二、姿态检测三、3D物体案例检测案例 前言 Mediapipe是google的一个开源项目&#xff0c;用于构建机器学习管道。   提供了16个预训练模型的案例&#xff1a;人脸检测、…

基于级联深度学习算法的前列腺病灶检测在双参数MRI中的评估| 文献速递-基于深度学习的乳房、前列腺疾病诊断系统

Title 题目 Evaluation of a Cascaded Deep Learning–based Algorithm for Prostate Lesion Detection at Biparametric MRI 基于级联深度学习算法的前列腺病灶检测在双参数MRI中的评估 Background 背景 Multiparametric MRI (mpMRI) improves prostate cancer (PCa) de…

从西安出发,走向世界——西安国际数字影像产业园跻身全国十大产业园区行列

西安&#xff0c;作为中国历史文化名城&#xff0c;不仅以其丰富的历史遗产闻名于世&#xff0c;还逐渐成为现代科技和产业发展的新高地。产业园区成为推动经济增长和创新发展的重要引擎&#xff0c;西安国际数字影像产业园&#xff0c;正怀揣着雄心壮志&#xff0c;向着全国十…

idea git拉取代码can‘t update

idea有时候创建的新分支&#xff0c;提交以后却无法拉去代码&#xff0c;提示如下错误信息&#xff1a; cant update No tracked branch configured for branch 在idea的“Terminal”窗口中输入如下命令即可 git branch --set-upstream-to origin 找不到Terminal的可以参考下图…

ESP32S3 IDF 对 16路输入输出芯片MCP23017做了个简单的测试

这次还是使用了idf老版本4.4.7&#xff0c;上次用了5.3&#xff0c;感觉不好用&#xff0c;官方的MCP23017芯片是英文版&#xff0c;真的很难读明白&#xff0c;可能是我英语水平不够吧。先看看每个寄存器的功能&#xff1a; IODIRA 和 IODIRB: 输入/输出方向寄存器 IPOLA 和 I…

B端界面升级就是升级颜值,错了,这样想就片面啦。

在B端应用的发展中&#xff0c;界面升级是非常重要的一环。然而&#xff0c;界面级不仅仅是为了提升外观颜值&#xff0c;还需要关注用户体验、功能增强和效率提升等方面。 虽然美观的界面可以吸引用户的眼球&#xff0c;但如果功能不完善&#xff0c;用户可能会选择其他产品。…

LVS负载均衡(twenty-six day)

一、LVS &#xff08;一&#xff09;什么是LVS linux virtural server的简称&#xff0c;也就是linxu虚拟机服务器&#xff0c;这是一个由章文岩博士发起的开源项目&#xff0c;官网是http://www.linuxvirtualserver.org,现在lvs已经是linux内核标准的-部分&#xff0c;使用lv…

vue 实现上浮气泡效果

一、 效果 二、代码 1.用 li 来做每个气泡 <div class"dataSea_middle_bottom"><ul><liv-for"(item,index) in keyBusiness":key"index"class"pops animal"><p class"fb">{{ item.name}}</p>…

Java面试题(一)----Java基础

基础 Java中和equals有什么区别&#xff1f; 一个是运算符&#xff0c;一个是方法。 如果比较的对象是基本数据类型&#xff0c;则比较数值是否相等&#xff1b;如果比较的是引用数据类型&#xff0c;则比较的是对象的内存地址是否相等。 因为Java只有值传递&#xff0c;对于…