【C++】list容器的基本使用

news2024/11/13 14:43:57

一、list是什么

list的底层结构是带头双向循环链表。

相较于 vector 的连续线性空间,list 就显得复杂很多,它是由一个个结点构成,每个结点申请的空间并不是连续的,它的好处是每次插入或删除一个数据,就配置或释放一个元素空间,我们不用在意扩容问题了,也不用单独写扩容函数了,list对于任何位置的元素进行插入或者元素移除,时间复杂度都是O(1)。

二、list容器的基本使用

1、构造函数

这里的allocator是空间配置器,我们这里先不用管它(认为它不存在),等到后面的篇章会具体讲解它的作用。 其中,(1)是默认构造 (2)是n个val构造 (3)是迭代器区间构造 (4)是拷贝构造。

2、析构函数

释放每个结点申请的空间资源。

3、赋值重载

它的操作数是两个已有的list对象。

4、迭代器

(1)使用

list的迭代器和vector的迭代器在用法上一模一样。

使用迭代器可以进行遍历:

#include <iostream>
#include <list>
using namespace std;

void test_list1()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);

	list<int>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

int main()
{
	test_list1();
	return 0;
}

运行结果:

 

支持迭代器,就支持范围for,因为范围for的底层就是迭代器。

void test_list1()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;
}

int main()
{
	test_list1();
	return 0;
}

运行结果:

 

(2)分类

迭代器从功能上进行分类,分为iterator、const_iterator、reverse_iterator、const_reverse_iterator这四类,它还可以从性质上进行分类,分为单向迭代器、双向迭代器、随机迭代器。

单向迭代器包括(forward):forward_list / unordered_map... 它只支持++

双向迭代器(bidirectional)包括:list / map / set... 它只支持++ / --

随机迭代器(random access)包括:vector / string / deque... 它支持++ / -- / + / -

它们的底层结构决定它们的性质,它们的底层结构也决定它们可以使用哪些算法。

比如:

C++标准库中sort的模板参数是RandomAccessIterator,从它的名字中可以看出它只接收随机迭代器,如果传一个单向迭代器,它也可以接收,但运行时会发生错误即程序不能正常运行。

C++标准库中reverse的模板参数是BidirectionalIterator,从它的名字中可以看出它只接收双向迭代器,如果传一个单向迭代器,它也可以接收,但运行时会发生错误即程序不能正常运行。如果传一个随机迭代器,也可以正常使用,因为随机迭代器的范围更广,凡是支持单向迭代器或者双向迭代器的都支持随即迭代器。

出现InputIterator,说明单向/双向/随机迭代器都可以使用这个函数。

5、成员函数

(1)empty()

判断容器内节点个数是否为空,若为空返回true,否则返回false。

(2)size()

返回容器内节点个数。

(3)front()

reference 就是list<T>中的T,T是模板参数。front就是用来返回第一个结点的值。

(4)back()

 返回最后一个结点的值。

(5)assign()

用于赋值。(1)是用一段迭代区间赋值 (2)是用n个val赋值

(6)push_front()

头插,头部插入一个新结点。

(7)pop_front()

头删,删除头部的结点。 

(8)push_back()

尾插,尾部插入一个新结点。

(9)pop_back()

尾删,删除尾部的结点。

(10)emplace_back()

它的功能和push_back()差不多,但有不同之处,现阶段我们就把它当作push_back()使用。

struct AA
{
	AA(int a1, int a2)
		:_a1(a1)
		, _a2(a2)
	{}
private:
	int _a1;
	int _a2;
};

void test_list2()
{
	list<AA> lt;

	AA aa1(1, 1);
	lt.push_back(aa1);
	lt.push_back(AA(2,2));
	//lt.push_back(3,3); //err,这里会报错


	lt.emplace_back(aa1);
	lt.emplace_back(AA(2,2));
	lt.emplace_back(3,3); //ok,与push_back()的不同之处,它支持直接传构造A对象的参数
}

int main()
{
	test_list2();
	return 0;
}

(10)insert()

(1)是在pos位置前插入val (2)是在pos位置前插入n个val (3)是在pos位置前插入一段迭代区间。 

void test_list3()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);
	for (auto e : lt)
		cout << e << " ";
	cout << endl;

	//在第3个位置前(整体位置是从0开始编号的)插入30
	//lt.insert(lt.begin() + 3, 30); //list容器不支持+,所以这行代码会报错

	list<int>::iterator it = lt.begin();
	int k = 3;
	while (k--)
	{
		++it;//list容器支持++,所以这行代码会报错
	}
	lt.insert(it, 30);  //(1)
	for (auto e : lt)
		cout << e << " ";
	cout << endl;
}

int main()
{
	test_list3();
	return 0;
}

运行结果: 

(11)erase()

 (1)删除pos位置上的值 (2)删除一段迭代区间

void test_list4()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);
	for (auto e : lt)
		cout << e << " ";
	cout << endl;

	int x;
	cin >> x;
	list<int>::iterator it = find(lt.begin(),lt.end(),x);
	if (it != lt.end()) //找到了
		lt.erase(it); //(1)
	else
		cout << "不存在:" << x << endl;
	for (auto e : lt)
		cout << e << " ";
	cout << endl;

}

int main()
{
	test_list4();
	return 0;
}

输入4,运行结果: 

(12)clear()

清空所有结点数据。

(13)resize()

将结点个数更新到n,假设原先节点个数为k。

1、k > n

释放k - n个结点

2、k < n

增加n - k个节点,结点中的内容自己可以传,也可以不传,用默认的缺省值。

因为list的底层是链表,所以没有扩容这一说法。

(14)reverse()

逆置结点的顺序。

void test_list5()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);
	for (auto e : lt)
		cout << e << " ";
	cout << endl;

	lt.reverse();
	for (auto e : lt)
		cout << e << " ";
	cout << endl;

    //reverse(lt.begin(),lt.end()); //这里的算法库中的reverse也支持list容器的逆置
}

int main()
{
	test_list5();
	return 0;
}

运行结果:

 

(15)sort()

 由于算法库中的sort不支持list容器的迭代器,所以这里它自己实现了一个。

void test_list6()
{
	list<int> lt;
	lt.push_back(20);
	lt.push_back(40);
	lt.push_back(50);
	lt.push_back(30);
	lt.push_back(10);
	for (auto e : lt)
		cout << e << " ";
	cout << endl;

	//默认排升序
	lt.sort(); //(1)
	for (auto e : lt)
		cout << e << " ";
	cout << endl;

}

int main()
{
	test_list6();
	return 0;
}

运行结果: 

 

如果想要排降序,就要借助仿函数:

void test_list6()
{
	list<int> lt;
	lt.push_back(20);
	lt.push_back(40);
	lt.push_back(50);
	lt.push_back(30);
	lt.push_back(10);
	for (auto e : lt)
		cout << e << " ";
	cout << endl;

	//排降序
	greater<int> gt;
	lt.sort(gt);
	for (auto e : lt)
		cout << e << " ";
	cout << endl;

}

int main()
{
	test_list6();
	return 0;
}

运行结果:

 

(16)merge()

合并两个链表,前提是这两个链表需要有序。 

void test_list7()
{
	list<int> first, second;

	first.push_back(3);
	first.push_back(6);
	first.push_back(1);

	second.push_back(2);
	second.push_back(5);
	second.push_back(4);

	first.sort();
	second.sort();

	first.merge(second); //合并后,second这个链表就空了,相当于将它移到first上了

	for (auto e : first)
		cout << e << " ";
	cout << endl;

	cout << "----------" << endl;

	for (auto e : second) //second中已经没有数据了
		cout << e << " ";
	cout << endl;

	cout << "----------" << endl;
}

int main()
{
	test_list7();
	return 0;
}

运行结果: 

 

 (17)unique()

去重,删除容器中与val值一样的结点,只保留第一个。删除时要保证重复的数据紧挨着,否则会删不干净,建议删除前先排序。

void test_list8()
{
	list<int> lt;
	lt.push_back(5);
	lt.push_back(1);
	lt.push_back(4);
	lt.push_back(2);
	lt.push_back(4);
	lt.push_back(3);
	lt.push_back(3);
	lt.push_back(5);
	for (auto e : lt)
		cout << e << " ";
	cout << endl;

	lt.sort();

	lt.unique();
	for (auto e : lt)
		cout << e << " ";
	cout << endl;
}

int main()
{
	test_list8();
	return 0;
}

运行结果: 

(18)remove()

对val进行查找,如果找到就删除,否则什么也不做。 

(19)remove_if()

给定一个条件,如果满足就删除,否则什么也不做。

(20)splice()

 (1)是将x中全部结点转移到当前容器中的pos位置之前的位置处 (2)是将x中i位置的结点转移到当前容器中的pos位置之前的位置处 (3)是将x中的一段迭代区间上的结点转移到当前容器中的pos位置之前的位置处

//将一个链表的结点转移给另一个链表
void test_list9()
{
    list<int> mylist1, mylist2;
    list<int>::iterator it;

    //先插入一些值
    for (int i = 1; i <= 4; ++i)
        mylist1.push_back(i);      // mylist1: 1 2 3 4

    for (int i = 1; i <= 3; ++i)
        mylist2.push_back(i * 10);   // mylist2: 10 20 30

    cout << "初始值:" << endl;
    for (auto e : mylist1)
        cout << e << " ";
    cout << endl;
    for (auto e : mylist2)
        cout << e << " ";
    cout << endl;

    cout << "---------------" << endl;

    it = mylist1.begin();
    ++it;                     

    mylist1.splice(it, mylist2); //(1)
    // mylist1: 1 10 20 30 2 3 4
    // mylist2中的数据已经没有了
    // "it" 仍然指向2 

    cout << "第一次调用splice后:" << endl;
    for (auto e : mylist1)
        cout << e << " ";
    cout << endl;
    for (auto e : mylist2)
        cout << e << " ";
    cout << endl;

    cout << "---------------" << endl;

    mylist2.splice(mylist2.begin(), mylist1, it); //(2)
    // mylist1: 1 10 20 30 3 4
    // mylist2: 2
    // "it"现在是无效的
    cout << "第二次调用splice后:" << endl;
    for (auto e : mylist1)
        cout << e << " ";
    cout << endl;
    for (auto e : mylist2)
        cout << e << " ";
    cout << endl;

    cout << "---------------" << endl;
}

int main()
{
	test_list9();
	return 0;
}

运行结果: 

根据它的功能,我们还可以调整当前链表的结点顺序:

//调整当前链表结点的顺序
void test_list10()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);
	lt.push_back(6);

	cout << "转移前:" << endl;
	for (auto e : lt)
		cout << e << " ";
	cout << endl;

	//现要求将6这个结点转移到1这个结点的前面去
	lt.splice(lt.begin(), lt, --lt.end());

	cout << "转移后:" << endl;
	for (auto e : lt)
		cout << e << " ";
	cout << endl;
}

int main()
{
    test_list10();
    return 0;
}

三、排序效率

这里我们将list容器中的sort与算法库中的sort进行效率对比,看看哪个更优。

(1)对比一

#include <vector>
#include <iostream>
#include <algorithm>
#include <list>
using namespace std;

void test_op1()
{
	srand((unsigned int)time(0));
	const int N = 1000000;
	list<int> lt1;
	vector<int> v;

	for (int i = 0;i < N;++i) //随机生成100万个数据
	{
		auto e = rand() + i;
		lt1.push_back(e);
		v.push_back(e);
	}

	int begin1 = clock();
	sort(v.begin(), v.end());
	int end1 = clock();

	int begin2 = clock();
	lt1.sort();
	int end2 = clock();

	cout << "vector sort:" << end1 - begin1 << endl;
	cout << "list sort:" << end2 - begin2 << endl;
}

int main()
{
	test_op1();
	return 0;
}

在Debug环境下的运行结果:

可以看到list排序优势更大,那是因为在Debug环境下,优化不是很大,运行结果不具有参考价值,在release环境下,双方优化都是最大的,这才公平。

在Realse环境下运行结果:

实际上,算法库中的sort效率是更好的。 

(2)对比二

#include <vector>
#include <iostream>
#include <algorithm>
#include <list>
using namespace std;

void test_op2()
{
	srand((unsigned int)time(0));
	const int N = 1000000;
	list<int> lt1;
	list<int> lt2;

	for (int i = 0;i < N;++i)
	{
		auto e = rand() + i;
		lt1.push_back(e);
		lt2.push_back(e);
	}

	int begin1 = clock();
	vector<int> v(lt2.begin(), lt2.end()); //迭代器区间构造
	sort(v.begin(), v.end()); //用算法库中的sort进行排序
	lt2.assign(v.begin(), v.end()); //再拷贝回lt2
	int end1 = clock();

	int begin2 = clock();
	lt1.sort(); //直接调用list中的sort进行排序
	int end2 = clock();

	cout << "list copy vector sort copy list sort:" << end1 - begin1 << endl;
	cout << "list sort:" << end2 - begin2 << endl;
}

int main()
{
	test_op2();
	return 0;
}

在Release环境下运行结果: 

这差距就出来了, 在begin1~end1这段时间内做的事情比begin2~end2这段时间内做的还要多,结果花费的时间还少,说明了当数据量比较大时,list中的sort排序效率很低。不如先把它的值拷贝给vector容器,然后排序,排完再拷贝回来。

四、结语

本篇内容到这里就结束了,主要讲了list容器的基本使用,希望对大家有些许收获,祝大家天天开心!

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

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

相关文章

WebServer:log

超时锁的编写 这个问题处于blockqueue.h文件中&#xff0c;内容如下&#xff1a; template<class T> bool BlockDeque<T>::pop(T& item, int timeout) {std::unique_lock<std::mutex> locker(mtx_);while(deq_.empty()) {if(condConsumer_.wait_for(lo…

内存泄漏

文章目录 内存泄漏发现问题topVisualVMArthas 原因分析代码层面并发请求 诊断问题MAT原理 –支配树获取运行时快照 内存泄漏 内存泄漏&#xff08;memory leak&#xff09;&#xff1a;在Java中如果不再使用一个对象&#xff0c;但是该对象依然在GC ROOT的引用链上&#xff0c;…

12.第二阶段x86游戏实战2-CE找基地址

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要…

全网最全:企业微信用户授权登录对接完整流程

Hello&#xff01;欢迎各位新老朋友来看小弟博客&#xff0c;祝大家事业顺利&#xff0c;财源广进&#xff01;&#xff01; 主题&#xff1a;企业微信用户授权与校验完整对接流程 一&#xff1a;构造第三方应用授权链接 如果第三方应用需要在打开的网页里面携带用户的身份信息…

吸尘器制造5G智能工厂物联数字孪生平台,推进制造业数字化转型

吸尘器制造行业&#xff0c;作为传统制造业的重要组成部分&#xff0c;也在积极探索如何通过先进技术实现生产模式的创新升级。5G智能工厂与物联数字孪生平台的融合应用&#xff0c;为吸尘器制造业的数字化转型铺设了一条高速通道&#xff0c;不仅极大地提升生产效率&#xff0…

华为---代理ARP简介及示例配置

目录 1. 概念 2. 前提条件 3. 使用环境 4. 工作过程 5. 优点 6. 缺点 7. 示例配置 7.1 示例场景 7.2基本配置 7.3 配置端口隔离 7.4 开启代理ARP 7.4.1 VLAN内代理ARP 7.4.2 VLAN间代理ARP 7.4.3路由式ARP代理 1. 概念 代理ARP&#xff08;Proxy ARP&#xff09;&…

GAMES202 作业1

参考&#xff1a;games202作业1 SM 首先是利用shadow map去生成尝试生成硬阴影。根据作业的要求 我们完成光源对物体的mvp矩阵 CalcLightMVP(translate, scale) {let lightMVP mat4.create();let modelMatrix mat4.create();let viewMatrix mat4.create();let projection…

Bigemap GIS Office 2024注册机 全能版地图下载软件

对于需要利用GIS信息进行编辑、设计的用户来说&#xff0c;Bigemap GIS Office占有重要地位。用户可以使用Bigemap GIS Office作为工具进行设计、分析、共享、管理和发布地理信息。Bigemap GIS Office能实现多种数据流转、嵌入、融合以及更多地为用户提供数据的增强处理及多种分…

文心一言 VS 讯飞星火 VS chatgpt (351)-- 算法导论24.1 2题

二、证明推论24.3。推论 24.3 的内容是设 G ( V , E ) G(V,E) G(V,E)是一带权重的源结点为 s s s的有向图&#xff0c;其权重函数为 ω : E → R ω:\boldsymbol{E→R} ω:E→R。假定图 G G G不包含从源结点 s s s可以到达的权重为负值的环路&#xff0c;则对于所有结点 v ∈ …

完美转发、C++11中与线程相关的std::ref

目录 模板中的万能引用 std::forward实现完美转发 C11中与线程相关的std::ref 线程函数参数 用函数指针作为线程函数 用lambda表达式作为线程函数 模板中的万能引用 void Func(int& x) {cout << "左值引用" << endl; } void Func(int&&am…

spark之不同序列化对比

一&#xff0c;spark的rdd的序列话不同介绍 下面是使用不同序列化后的占用资源和数据大小 2&#xff0c;sparksql中序列化的区别 sparksql中使用序列化和不使用差别不大&#xff0c;英文sparksql中默认使用了encode自己实现的序列化方法&#xff0c;加上与不加序列化差别不大…

基于PHP+MySQL组合开发的在线客服源码系统 聊天记录实时保存 带完整的安装代码包以及搭建部署教程

系统概述 随着互联网技术的飞速发展&#xff0c;企业与客户之间的沟通方式日益多样化&#xff0c;在线客服系统作为连接企业与客户的桥梁&#xff0c;其重要性不言而喻。然而&#xff0c;市场上现有的在线客服系统往往存在成本高、定制性差、维护复杂等问题。针对这些痛点&…

Zabbix 6.4添加中文语言

/usr/share/zabbix/include/locales .inc .phplocale -agrep “zh_CN" yum install langpacks-zh_CN.noarch y y y

【千帆AppBuilder】零代码+组件+代码节点方式实现AI应用《法定退休年龄计算器》

欢迎来到《小5讲堂》 这是《千帆》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 背景创建应用基本信息角色指令引导信息 组件整体界面开始节点代码节…

大腾智能3D协同平台通过华为云云软件认证

在数字化浪潮的推动下&#xff0c;工业软件不仅是研发和生产的核心工具&#xff0c;更是创新突破的基础&#xff0c;正成为推动工业领域数字化转型的关键力量。 近日&#xff0c;深圳市大腾信息技术有限公司凭借在技术创新与产品优化方面的卓越表现&#xff0c;再次迎来里程碑…

望繁信科技受邀出席ACS2023,为汽车行业数智化护航添翼

2023年5月25-26日&#xff0c;ACS2023第七届中国汽车数字科技峰会在上海成功举行。此次峰会汇聚了众多汽车领域的顶级专家、产业链代表及企业高管&#xff0c;共同探讨当今汽车产业的转型与未来发展趋势。 作为唯一受邀的流程挖掘厂商代表&#xff0c;望繁信科技携最新行业优势…

对于C++继承中子类与父类对象同时定义其析构顺序的探究

思考这样一串代码的运行结果&#xff1a; #include <iostream> using namespace std; class Person { public:~Person() { cout << "~Person()" << endl; } }; class Student:public Person { public:~Student() { cout << "~Student(…

【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略

文章目录 C类与对象前言读者须知RVO 与 NRVO 的启用条件如何确认优化是否启用&#xff1f; 1. 按值传递与拷贝省略1.1 按值传递的概念1.2 示例代码1.3 按值传递的性能影响1.3.1 完全不优化 1.4 不同编译器下的优化表现1.4.1 Visual Studio 2019普通优化1.4.2 Visual Studio 202…

828华为云征文|基于华为云Flexus X实例快速搭建Halo博客平台

目录 前言 一、Flexus云服务器X介绍 1.1 Flexus云服务器X实例简介 1.2 Flexus云服务器X实例特点 1.3 Flexus云服务器X实例场景需求 二、Flexus云服务器X购买 2.1 Flexus X实例购买 2.2 购买MySQL加速镜像 2.3 重置密码 2.4 登录服务器 三、Flexus X实例安装Docker 3.1 系统版本…

小小扑克牌算法

1.定义一个扑克牌类Card&#xff1a; package democard; public class Card {public String suit;//表示花色public int rank;//表示牌点数Overridepublic String toString() {return "{"suit rank"}";}//实例方法&#xff0c;初始化牌的点数和花色public…