C++必修:STL之forward_list与list的使用

news2024/11/15 12:56:11

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C++学习
贝蒂的主页:Betty’s blog

1. forward_list与list

forward_list 是 C++ 11 引入的一种容器,它是一种带有头节点的单向链表。使用时需要包含头文件#include<forward_list>,其特点为:

  • 内存使用高效,因为只维护单向的链接。
  • 擅长在头部进行插入和删除操作,时间复杂度为 O(1)。

img

下面是forward_list用法的简单示例:

#include <iostream>
using namespace std;
#include <forward_list>
int main() 
{
    forward_list<int> fl = {1, 2, 3};
    // 在头部插入元素
    fl.push_front(0);

    // 遍历并输出
    for (int num : fl) 
    {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}

list 是 C++ 标准库中基于带头双向循环链表实现的容器。使用时需要包含头文件#include<list>,其特点为:

  • 动态大小:可以根据需要自动增长或收缩。
  • 高效的插入和删除操作:在任何位置进行插入和删除操作的时间复杂度都是近似 O(1)。
  • 双向遍历:支持双向迭代器,可以从前往后和从后往前遍历。

img

下面是list用法的简单示例:

#include <iostream>
#include <list>
using namespace std;
int main() 
{
    list<int> myList = {10, 20, 30, 40, 50};
    // 在头部插入元素
    myList.push_front(5);
    // 在尾部插入元素
    myList.push_back(60);
    // 遍历并输出
    for (int num : myList) {
        cout << num << " ";
    }
    cout << endl;

    // 删除指定元素
    myList.remove(30);

    // 再次遍历输出
    for (int num : myList) {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}

forward_listlist 都是用于存储序列的数据结构,但有一些明显的区别。

  • 结构:forward_list 是单向链表,list 是双向链表。
  • 迭代器:forward_list 只提供前向迭代器,而 list 提供双向迭代器,使用更灵活。
  • 内存占用:一般情况下,forward_listlist 内存占用更少。

在实际应用中,如果只需要前向遍历且对内存使用要求较高,优先选择 forward_list;如果需要双向遍历和更灵活的操作,list 则更合适。

因为forward_list日常中并不常用,所以这里就不在详细介绍,如果有需求可以直接去查官方文档即可。下面就让我们来介绍一下list的一些基本用法。

2. list的接口

list的接口也有很多,我们学习时也需要借助相关的文档——list的用法

img

2.1. list的迭代器

2.1.1. begin()与end()函数

list 迭代器的begin()rend() 的使用方法具体如下:

  1. 函数声明
  • iterator begin();
  • const_iterator begin() const;
  1. 作用:返回指向列表第一个元素的迭代器。
  2. 返回值:普通对象返回 iterator 迭代器,const 对象返回 const_iterator 迭代器。
  1. 函数声明:
  • iterator end();
  • const_iterator end() const;
  1. 作用:返回指向列表最后一个元素下一个位置(头节点)的迭代器。
  2. 返回值:普通对象返回 iterator 迭代器,const 对象返回 const_iterator 迭代器。

img

示例:

void Test1() 
{
	list<int> numbers = { 1, 2, 3, 4, 5 };
	list<int>::iterator it = numbers.begin();
	cout << "First element: " << *it << endl;
	while (it != numbers.end())
	{
		cout << *it <<" ";
		++it;
	}
	// 注意:这里不能直接解引用it,因为此时它指向的是头节点
	cout << endl;
}

img

2.1.2. rbegin()与rend()函数

list迭代器的rbegin()rend() 的使用方法具体如下:

  1. 函数声明:
  • reverse_iterator rbegin();
  • const_reverse_iterator rbegin() const;
  1. 作用:返回指向列表最后一个元素的反向迭代器。
  2. 返回值:普通对象返回 reverse_iterator 迭代器,const 对象返回 const_reverse_iterator 迭代器。
  1. 函数声明:
  • reverse_iterator rend();
  • const_reverse_iterator rend() const;
  1. 作用:返回指向列表第一个元素前一个位置(头节点)的反向迭代器。
  2. 返回值:普通对象返回 reverse_iterator 迭代器,const 对象返回 const_reverse_iterator 迭代器。

img

示例:

void Test2() 
{
    list<int> numbers = {1, 2, 3, 4, 5};
    list<int>::reverse_iterator rit = numbers.rbegin();
    cout << "Last element: " << *rit << endl;
    while (rit!= numbers.rend()) 
    {
        cout << *rit << " ";
        ++rit;
    }
    cout << endl;
}

img

2.2. list的初始化与销毁

因为list是一个类,所以我们在初始化时肯定调用其构造函数初始化。以下就是我们常见初始化的接口:

img

img

下面是具体的代码示例:

void Test3() 
{
    // 默认构造函数
    list<int> numbers1; 
    cout << "默认构造: ";
    for (const auto& num : numbers1) {
        cout << num << " ";
    }
    cout << endl;
    // n个val构造
    list<int> numbers2(5, 10); 
    cout << "n个val构造: ";
    for (const auto& num : numbers2) {
        cout << num << " ";
    }
    cout << endl;

    int arr[] = {1, 2, 3};
    // 通过vector的迭代器初始化
    list<int> numbers3(arr, arr + sizeof(arr) / sizeof(arr[0])); 
    cout << "迭代器区间构造: ";
    for (const auto& num : numbers3) {
        cout << num << " ";
    }
    cout << endl;

    list<int> numbers4 = {4, 5, 6};
    // 拷贝构造
    list<int> numbers5(numbers4); 
    cout << "拷贝构造: ";
    for (const auto& num : numbers5) {
        cout << num << " ";
    }
    cout << endl;

    numbers1 = numbers2;
    // 赋值重载
    cout << "赋值重载: ";
    for (const auto& num : numbers1) {
        cout << num << " ";
    }
    cout << endl;
}

img

而由于list是一个类,出了作用域会自动调用它的析构函数,所以不用显示调用。

2.3. list的容量操作

接下来我们将学习关于list类常见的容量操作:

函数名称功能
size返回列表中元素的数量
max_size返回列表可容纳的最大元素数量
empty检查列表是否为空,是则返回 true,否则返回 false
resize重新设置列表中元素的数量,超过原来数量则用默认值填充
clear清空列表中的所有元素
2.3.1. size()与max_size()

size 用于获取列表中当前元素的数量。max_size 返回列表能够容纳的最大元素数量。

void Test4() 
{
    list<int> numbers = {1, 2, 3, 4, 5};
    cout << "Size of list: " << numbers.size() << endl;
    cout << "Max size of list: " << numbers.max_size() << endl;
}

img

2.3.2. empty()和clear()

empty 用于检查列表是否为空。clear 用于清空列表中的所有元素。

void Test5() 
{
    list<int> numbers;
    if (numbers.empty()) 
    {
       cout << "List is empty" << endl;
    }
    numbers = {1, 2, 3};
    numbers.clear();
    if (numbers.empty()) 
    {
        cout << "List is cleared and now empty" << endl;
    }
}

img

2.3.3. resize()的用法

resize 用于重新设置列表中元素的数量。

当使用 resize(n) 时,如果 n 大于当前列表的大小,那么会在列表末尾添加足够数量的默认值元素,使列表大小达到 n 。如果 n 小于当前列表的大小,那么会从列表末尾删除一些元素,使列表大小变为 n

void Test6() 
{
    list<int> numbers = {1, 2, 3};
    numbers.resize(5);
    cout << "After resizing to 5: ";
    for (auto& num : numbers) 
    {
        cout << num << " ";
    }
    cout << endl;

    numbers.resize(2);
    cout << "After resizing to 2: ";
    for (auto& num : numbers) 
    {
        cout << num << " ";
    }
    cout << endl;
}

img

2.4. list的访问操作

接下来我们就来介绍list常见的访问函数:

函数名称功能
back返回列表最后一个元素
front返回列表第一个元素
void Test7() 
{
    list<int> myList = {10, 20, 30};  // 创建一个包含元素的列表

    // 输出列表的第一个元素
    cout << "The front element is: " << myList.front() << endl; 

    // 输出列表的最后一个元素
    cout << "The back element is: " << myList.back() << endl; 
}

img

2.5. list的修改操作

list关于修改的函数的接口都比较多,一一列举比较麻烦,这里我们只重点介绍常用的接口,剩下的大家具体使用时查官方文档即可。下面是常见的关于list修改的函数接口:

函数名称功能
push_back在列表尾部添加元素
push_front在列表头部添加元素
pop_back删除列表最后一个元素
pop_front删除列表第一个元素
insert在指定位置插入元素
erase删除指定位置或区间的元素
swap交换两个列表
2.5.1. push_back()与pop_back()

这两个函数就是简单的尾插与尾删。

void Test8() 
{
    list<int> myList;  // 创建一个空列表

    myList.push_back(10);  // 在列表尾部添加元素 10
    myList.push_back(20);  // 在列表尾部添加元素 20

    cout << "列表元素: ";
    for (auto& num : myList) {
        cout << num << " ";
    }
    cout << endl;

    myList.pop_back();  // 删除列表尾部的元素

    cout << "删除尾部元素后列表元素: ";
    for (auto& num : myList) {
        cout << num << " ";
    }
    cout << endl;
}

img

2.5.2. push_front与pop_front()

这两个函数就是简单的头插与头删。

void Test9() 
{
    list<int> myList;  // 创建一个空列表
    myList.push_front(5);  // 在列表头部添加元素 5
    myList.push_front(3);  // 在列表头部添加元素 3
    cout << "列表元素: ";
    for (auto& num : myList) {
        cout << num << " ";
    }
    cout << endl;
    myList.pop_front();  // 删除列表头部的元素
    cout << "删除头部元素后列表元素: ";
    for (auto& num : myList) 
    {
        cout << num << " ";
    }
    cout << endl;
}

img

2.5.3. insert()与erase()

当在列表中进行插入操作时,如果在一个迭代器指向的位置之前或之后插入元素,那么这个迭代器就会失效。所以我们需要接受insert的返回值,该返回值是一个指向新插入元素的迭代器。

void Test10()
{
	list<int> myList = { 1, 2, 3 };
	list<int>::iterator it = myList.begin();
	it = myList.insert(it, 4);  // 这里迭代器 it 失效
	it = myList.insert(it, 5);  // 这里迭代器 it 失效
	for (auto& num : myList)
	{
		cout << num << " ";
	}
	cout << endl;
}

img

使用erase函数删除元素后,指向被删除元素的迭代器也会失效,同样需要接受返回值,该返回值是一个指向删除元素后一个元素的迭代器。

void Test11()
{
	list<int> myList = { 1, 2, 3, 4, 5 };
	list<int>::iterator it = myList.begin();
	it = myList.erase(it);  // 迭代器 it 失效
	it = myList.erase(it);  // 迭代器 it 失效
	for (auto& num : myList)
	{
		cout << num << " ";
	}
	cout << endl;
}

img

2.5.4. swap()交换

listswap交换也与之前vectorstring中的swap一样只是指针交换,效率很高。

void Test12()
{
	list<int> list1 = { 1, 2, 3 };
	list<int> list2 = { 4, 5, 6 };
	cout << "交换之前:" << endl;
	for (auto& num : list1)
	{
		cout << num << " ";
	}
	cout << endl;

	for (auto& num : list2)
	{
		cout << num << " ";
	}
	cout << endl;
	list1.swap(list2);
	cout << "交换之前:" << endl;
	for (auto& num : list1)
	{
		cout << num << " ";
	}
	cout << endl;

	for (auto& num : list2)
	{
		cout << num << " ";
	}
	cout << endl;

}

img

2.6. list的其他操作

下面我们接受一些关于list常见的其他操作。

函数名称功能描述
splice将元素从一个列表转移到另一个列表
remove移除具有特定值的元素
remove_if移除满足条件的元素
unique移除重复的值
merge合并已排序的列表
sort对容器中的元素进行排序
reverse反转元素的顺序

splice() 函数主要用于在列表中进行元素的转移操作。 它可以将一个列表中的部分或全部元素转移到另一个列表中。可以指定要转移的元素范围以及目标插入位置等,实现了高效灵活的元素移动和重组。

void Test13() 
{
    list<int> list1 = {1, 2, 3};
    list<int> list2 = {4, 5};

    // 将 list2 的元素转移到 list1 中
    list1.splice(list1.end(), list2);

    for (auto num : list1) {
        cout << num << " ";
    }
    cout << endl;
}

img

remove函数相当于一直遍历列表,然后erase删除指定元素。

void Test14() 
{
    list<int> myList = {1, 2, 2, 3, 2};

    // 移除值为 2 的元素
    myList.remove(2);

    for (auto num : myList) {
        cout << num << " ";
    }
    cout << endl;
}

img

remove_if函数相当于remove的补充,它支持传参函数或者仿函数。

bool isEven(int num) {
    return num % 2 == 0;
}

void Test15() {
    list<int> myList = {1, 2, 3, 4, 5, 6};

    // 移除满足偶数条件的元素
    myList.remove_if(isEven);

    for (auto num : myList) {
        cout << num << " ";
    }
    cout << endl;
}

img

unique 函数主要用于移除列表中相邻的重复元素。它使得容器中只保留不重复的元素序列,但需要注意的是,它并不保证完全去除所有重复元素,只是处理相邻的重复项,通常也需要结合其他操作来实现完全去重。

void Test16() 
{
    list<int> myList = {1, 2, 2, 3, 3, 3};

    // 移除相邻的重复元素
    myList.unique();
    for (auto num : myList) {
        cout << num << " ";
    }
    cout << endl;
}

img

merge()函数主要用于将两个已排序的序列合并成一个新的已排序序列。 它会按照排序顺序将一个序列中的元素与另一个序列中的元素合理地组合在一起,形成一个合并后的有序序列。需要注意的是,在合并之前,两个源序列本身需要是已经排序好的。

void Test17()
{
    list<int> list1 = {1, 3, 5};
    list<int> list2 = {2, 4, 6};

    list1.sort();
    list2.sort();

    // 合并两个已排序的列表
    list1.merge(list2);

    for (auto num : list1) {
        cout << num << " ";
    }
    cout << endl;
}

img

list中的 sort() 函数用于对列表进行排序。它会按照指定的排序规则(默认为升序)对列表中的元素进行重新排列,使得元素按有序的方式呈现。

需要注意的是算法库中也有一个sort()函数,但是其支持的是随机迭代器,而list是一种双向迭代器,所以list无法调用算法库中的sort()

void Test18() {
    list<int> myList = {3, 1, 4, 1, 5, 9, 2, 6, 5};

    // 对列表进行排序
    myList.sort();

    for (auto num : myList) {
        cout << num << " ";
    }
    cout << endl;
}

最后还有一个reverse()函数,这个函数在算法库中也有,也可以用于实现list的逆置。

void Test19()
{
	list<int> l2 = { 1,2,4,5 };
	l2.reverse();//list中的reverse
	reverse(l2.begin(), l2.end());//算法库中的reverse
	for (auto& num : l2)
	{
		cout << num << " ";
	}
}

img

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

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

相关文章

LQR横向控制及融合PID纵向控制C++实现

目录 简介一、现代控制理论1.1 经典控制理论和现代控制理论的区别1.2 全状态反馈控制系统 二、LQR控制器2.1 连续时间2.1.1 Q、R矩阵的选取2.1.2 推导过程2.1.3 连续时间下的LQR算法步骤 2.2 离散时间2.2.1 连续LQR和离散LQR的区别2.2.2离散时间下的LQR算法步骤 三、LQR实现自动…

AI大模型之旅--安装向量库milvus

milvus&#xff0c;向量索引库 1.milvus部署 milvus的官方文档中看到最新版本的部署方式Install Milvus Standalone with Docker Compose curl -sfL https://raw.githubusercontent.com/milvus-io/milvus/master/scripts/standalone_embed.sh -o standalone_embed.sh &#xf…

stm32f103c8t6与TB6612FNG解耦测试

stm32f103c8t6与TB6612FNG解耦测试 本文操作方式: 忽略底层,只做上层, 所以前面全部照搬步骤,重在调试 文章目录 stm32f103c8t6与TB6612FNG解耦测试本文操作方式:创建基本工程(1)跳转此链接,创建(2)创建电机驱动文件夹(3)PWM原理(4)电机转动控制 oled调试和key调试(5)OLED转速…

C++:奇异递归模板模式(CRTP模式)

奇异递归模板模式 文章目录 奇异递归模板模式理论说明CRTP模式的功能静态多态强制静态接口编译时多态优化解释 理论说明 奇异递归模板模式&#xff08;Curiously Recurring Template Pattern, CRTP&#xff09; 是一种设计模式&#xff0c;其原理很简单&#xff1a; 继承者将自…

工业三防平板赋能自动化产线打造工厂智慧管理

随着工业4.0时代的到来&#xff0c;智能制造成为了众多企业转型升级的必然选择。而MES系统作为智能制造的核心环节&#xff0c;能够有效地整合生产数据&#xff0c;提升生产效率&#xff0c;并实现工厂运营的数字化管理。然而&#xff0c;传统的MES系统大多依赖于PC端操作&…

关于vs调试的一些基本技巧方法,建议新手学习

文章目录 1.Debug 和 Release2.VS的调试快捷键3.对程序的监视和内存观察3.1监视3.2内存 4.编程常见错误归类4.1编译型错误4.2链接型错误4.3运行时错误 1.Debug 和 Release 在我们使用的编译器 vs 中&#xff0c;这个位置有两个选项&#xff0c;分别为Debug和Release&#xff0c…

开源应用:AI监测如何成为社会安全的智能盾牌

社会背景 随着社会的快速发展&#xff0c;社会安全管理正站在一个新时代的门槛上。社会对安全管理的需求不断增长&#xff0c;传统的安全措施已难以满足现代社会的需求。AI技术以其独特的数据处理和模式识别能力&#xff0c;正在成为我们社会安全的智能盾牌。 AI大模型识别功能…

【牛客】2024暑期牛客多校6 补题记录

文章目录 A - Cake&#xff08;树上dp&#xff09;B - Cake 2&#xff08;暴力&#xff09;D - Puzzle: Wagiri&#xff08;tarjan&#xff09;F - Challenge NPC 2&#xff08;构造&#xff09;H - Genshin Impacts Fault&#xff08;签到&#xff09;I - Intersecting Interv…

利用扩散模型DDPM生成高分辨率图像|(一)DDPM模型构建

利用扩散模型DDPM生成高分辨率图像&#xff08;生成高保真图像项目实践&#xff09; Mindspore框架利用扩散模型DDPM生成高分辨率图像|&#xff08;一&#xff09;关于denoising diffusion probabilistic model &#xff08;DDPM&#xff09;模型 Mindspore框架利用扩散模型DD…

数字音频工作站(DAW)FL Studio 24.1.1.4239中文破解版

FL Studio 24.1.1.4239中文破解版是一款功能强大的数字音频工作站&#xff08;DAW&#xff09;&#xff0c;它广泛应用于音乐创作和音乐制作领域。FL Studio是由比利时软件公司Image-Line开发的音乐制作软件&#xff0c;它拥有丰富的音效、合成器、采样器、鼓机等工具。FL Stud…

stm32cubemx+ADC的多通道轮询数据采集和DMA数据采集实现,亲测可用

ADC是单片机的重要组成&#xff0c;也是存在一定的难点。 一、多通道轮询数据采集。 1、配置时钟&#xff0c;用的无源晶振。 2、SW烧写方式 添加USART 3、ADC选择了四个通道 其中两个是采集电压&#xff0c;另外两个是采集芯片内部温度和参考电压。 4、配置采集模式 这里是…

萌啦数据官网丨萌啦ozon数据分析工具官网

在当今这个数据驱动的时代&#xff0c;电子商务的蓬勃发展离不开精准的数据分析与洞察。对于在OZON平台上耕耘的商家而言&#xff0c;掌握市场趋势、优化产品布局、提升运营效率成为了赢得竞争的关键。正是在这样的背景下&#xff0c;萌啦数据官网应运而生&#xff0c;作为一款…

信用卡使用雷区大揭秘:为何你贷款被拒?

​好多朋友明明条件挺好&#xff0c;但申请银行贷款时却吃了闭门羹&#xff0c;一查征信&#xff0c;原来是信用卡使用上栽了跟头。信用卡可是个关键角色&#xff0c;用得好助力贷款&#xff0c;用得不好&#xff0c;直接拖后腿。今天咱们就聊聊信用卡对贷款申请的影响情况和解…

鸿蒙OS ArkTS 省市县级联选择框,封装组件

背景&#xff1a; 公司现在要开发纯血鸿蒙版本APP&#xff0c;我被抽调过来做点功能。现在要做一个省市县级联选择框&#xff0c;并且要封装为组件&#xff0c;供其他页面模块使用。 效果图&#xff1a; 难点&#xff1a; 1. 现在官方文档上只是查到了TextPicker组件是可以做…

建筑设计遇上这几个工具,就是锦上添花!

声明&#xff1a;此篇为 ai123.cn 原创文章&#xff0c;转载请标明出处链接&#xff1a;https://ai123.cn/2161.html 当AI遇上建筑&#xff0c;设计界的火花就这样擦出来了&#xff01;&#x1f440; 身为一名内外饰设计工程师&#xff0c;你是否也在担心作品不经意间借鉴过了头…

Tomcat启动控制台乱码解决方案

前言 事情的起因是这样的&#xff0c;当时我用了阿里云osssdk里的代码下载文件&#xff0c;如下 ossClient.getObject(new GetObjectRequest(bucketName, objectName), new File(pathName)); &#xff0c;开始一切顺利&#xff0c;直到部署正式环境后&#xff0c;用了一段时间…

【Material-UI】Button 组件中的尺寸设置(Sizes)详解

文章目录 一、基础尺寸选项1. 小尺寸&#xff08;Small&#xff09;2. 中等尺寸&#xff08;Medium&#xff09;3. 大尺寸&#xff08;Large&#xff09; 二、尺寸的应用场景三、高级用法和最佳实践1. 使用主题调整默认尺寸2. 确保一致性3. 考虑无障碍设计 四、总结 在用户界面…

代码随想录算法训练营第五十二天|101.孤岛的总面积 、102.沉没孤岛 、103.水流问题 、104.建造最大岛屿

101. 孤岛的总面积 DFS搜索&#xff1a; dfs 函数是一个递归函数&#xff0c;用于深度优先搜索&#xff08;DFS&#xff09;遍历网格中的陆地区域。它将访问过的陆地标记为0&#xff0c;并统计陆地的数量。 我们首先定义了四个方向的移动偏移量 dir。 global count 语句用于声…

C++入门2

函数重载 函数重载&#xff1a;是函数的一种特殊情况&#xff0c;C允许在同一作用域中声明几个功能类似的同名函数&#xff0c;这 些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同&#xff0c;常用来处理实现功能类似数据类型 不同的问题 比如下面的 int add(int x…

数据结构和算法|递归算法那些事(递归算法的时间复杂度、尾递归优化、斐波那契数列)

对于文章的第一部分&#xff0c;递归算法的时间复杂度&#xff0c;来自于代码随想录文章:通过一道面试题目&#xff0c;讲一讲递归算法的时间复杂度&#xff01; 对于第二节尾递归优化来自于B站&#xff1a;尾递归优化&#xff1a;你的递归调用是如何被优化的&#xff1f; 文章…