【C++】list简单介绍

list基本功能介绍

前言

本篇不会讲太多细节,就说一下STL库中一些函数的基本用法,如果想要了解细节上的东西的话,建议看我string的介绍:string介绍

还是照着cplusplus的那个网站中的内容讲:STL -> list

正式开始

STL库中的链表是一个带头双向循环链表。也是存取元素最方便的链表。我前面也用C语言实现过这个带头双向循环链表,但是实现的话肯定是没有 C++ 的实现起来更加方便的,模拟实现的话,下一篇博客再说。

链表节点什么的基本概念就不说了,直接讲库中的函数接口。

构造函数

老样子,STL库中各个容器的实现都是有很多相似的地方的。

就还是那几样。无参的、n个val的、迭代区间的、拷贝构造。

再说析构,没啥好讲的,就是生命周期结束自动调用,释放空间。

然后说个常用的:push_back

push_back

尾插。直接上例子:

数据有了,看看怎么遍历。

iterator

和前面vector不同的是,list不是连续的空间了,所以不能支持简单的[ ]重载来进行任意位置的访问。所以我们此处想要遍历list的话,就只能用迭代器了。

或者是范围for,但是底层也是迭代器。

但是要注意,范围for中只是简单的赋值操作,可能会出现深拷贝的情况导致开销很大,这时如果我们不需要改变list中节点内容的话,尽量传const + 引用。

也可用迭代器修改。

list也有rbegin、rend还有const的。用法都和前面的string和vector一样。和这两个不一样的是list有push_front和pop_front。

push_front

空间不连续,尽管是头插,时间复杂度也是O(1),因为不需要挪动数据。

再说insert和erase。

insert

参数:(pos, val) 、(pos, n, val) 、(pos, first, last)这三样。

还是老样子,用的时候需要用find。

find用的时候还是迭代器区间,要找的值。

用完find后要判断是否找到。

直接上例子:

这里要注意一点,list的insert不存在迭代器失效的问题,因为list空间是不连续的,在某一结点前插入一个节点,原节点的地址是不会改变的。

所以我们可以多次在同一位置插入。

但是erase会出现迭代器失效的问题。

erase

先看一个正常的例子:

再说一个不正常的例子:

这里连续两次释放了同一块空间,但是因为list空间不连续,删除一个节点后其他节点的地址是不会变的,所以没有其他节点顶替这个被删除的节点。不像string和vector那样会有其他元素顶替上来。所以说删除了之后不就能再对这个节点进行任何操作了,不然就是非法访问。

看:

出错了。

然后剩下的那些基本函数别的容器中也有,就不说了。

说一下list自带的。

上面这几个函数了解就好,不需要死记,用的时候再查一下就好了。

splice

这个函数功能是转移数据,将一个链表中的数据转给另一个链表。

(pos, x),这个是把链表x中的数据转移到当前量表中的pos位置之前。

(pos, x, i),把x中第i位置处的元素转移在pos之前。

(pos, x, first, last),把x中[first, last)的元素转移到pos前。

注意,转移后x中对应的元素会消失。

就直接看官网中给的例子吧:

remove

除去list中的元素,看我圈红的部分,remove会把list中所有值为val的节点都删去。

例子:

unique

这个函数就是去重的功能。但是不是全部去重。而是将相连位置的元素去重,留下一个元素。

所以说,如果不考虑结果的元素顺序并且想要将所有重复元素去处的话,我们可以先排序,再用unique。

reverse

这个函数是list自带的。

这里带头双向循环链表,逆置的话,只需要依次遍历最左边和最右边,两边交换数值,就可实现逆置,不需要改链表的每个节点的指针指向。

也可以用算法库中的逆置,但是二者的实现原理是不一样的。

我这里测试1000000个数的逆置,结果如下:

所以说,还是用自带的reverse好。

sort

库中也有个sort,这里也有个sort。

但是二者效率不一样。

算法库中sort只能对连续空间的元素进行排序。因为sort底层用的是快排,有三数取中的优化,但是三数去中需要我们能够找到中间的值,而list想要找到中间值的话会非常麻烦,效率就会大大降低,但是若快排没有三数去中的话,当元素已经有序时,就会出大问题,时间复杂度会直接变为N^2的。

所以链表不能用算法库中的sort,只能用list提供的sort,而list提供的sort底层用的是归并排序。

N个数据需要排序,vector + 算法库中的sort快,还是list + 自带的sort快?

答案是vector + 算法库中的sort快。

我们用代码测试一下:(用release版

cpp 复制代码
void test_op()
{
	srand(time(0));
	const int N = 100000;
	vector<int> v;
	v.reserve(N);
	
	list<int> lt2;

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

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

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

	printf("vector sort:%d\n", end1 - begin1);
	printf("list sort:%d\n", end2 - begin2);
}

一百万个数:

可以看到vector + 算法库的sort速度还是比较快的。

现在我们用两个相同的list,一个用自带的sort,一个将所有的数拷贝到vector中再用拷贝后的vector加上算法库中的sort来排序,然后再把vector中的数拷贝到list中。

测试代码如下:

cpp 复制代码
void test_op()
{
	srand(time(0));
	const int N = 100000;
	vector<int> v;
	v.reserve(N);

	list<int> lt1;
	list<int> lt2;

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


	int begin1 = clock();
	// 拷贝到vector排序,排完以后再拷贝回来
	for (auto e : lt1)
	{
		v.push_back(e);
	}
	sort(v.begin(), v.end());
	size_t i = 0;
	for (auto& e : lt1)
	{
		e = v[i++];
	}
	int end1 = clock();

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

	printf("copy vector sort:%d\n", end1 - begin1);
	printf("list sort:%d\n", end2 - begin2);
}

同样是一百万个数,结果为:

可以看到,仍然比list自带的sort好的多。

那么可以说list库中给的sort基本上是废的,vector支持随机访问效率就会高上特别多。

那么当数据量大时,不如将list的数据拷贝到vector中,再用vector排序,速度都比list中的sort快好几倍。

merge

将两个有序链表合并,合并后的链表依然有序。

用这个前提是链表得有序。

如果有无序的,结果就是无序:

好了,该讲的都讲了,有不会的自行查文档即可。

到此结束。。。

相关推荐
erxij1 分钟前
【游戏引擎之路】登神长阶(十四)——OpenGL教程:士别三日,当刮目相看
c++·经验分享·游戏·3d·游戏引擎
Lizhihao_15 分钟前
JAVA-队列
java·开发语言
林开落L32 分钟前
前缀和算法习题篇(上)
c++·算法·leetcode
远望清一色33 分钟前
基于MATLAB边缘检测博文
开发语言·算法·matlab
何曾参静谧41 分钟前
「Py」Python基础篇 之 Python都可以做哪些自动化?
开发语言·python·自动化
Prejudices1 小时前
C++如何调用Python脚本
开发语言·c++·python
单音GG1 小时前
推荐一个基于协程的C++(lua)游戏服务器
服务器·c++·游戏·lua
我狠狠地刷刷刷刷刷1 小时前
中文分词模拟器
开发语言·python·算法
wyh要好好学习1 小时前
C# WPF 记录DataGrid的表头顺序,下次打开界面时应用到表格中
开发语言·c#·wpf
AitTech1 小时前
C#实现:电脑系统信息的全面获取与监控
开发语言·c#