List的学习

List

我们当前学习的主要是带头双向循环链表。其中一个原因是循环链表可以实现时间复杂度为O(1)的插入、删除等操作。

1、基本用法

无参构造:

cpp 复制代码
list<int> l1;

initializer_list构造:

cpp 复制代码
list<int> l2 = { 1,2,3,4 };

我们就可以用迭代器和范围for的方式,打印list序列:

2、插入、删除

由于list没有规定find()查找方法,我们就借用算法库的find()。

cpp 复制代码
list<int> l3 = { 1,2,3,4 };
auto it2 = find(l3.begin(), l3.end(), 3);

插入:

插入后,迭代器it2没有失效,因为list不存在扩容。

删除:

但是删除后,迭代器it2失效了,因为erase()删除的本质,是释放指定节点的空间。此时vs2022环境下会报警告:

3、一些方法

reverse():逆置

merge():合并

合并算法,类似于我们在数据结构算法中,将两个有序序列,合成一个有序序列的算法题。

只不过这里给出的序列,不一定是有序的,需要先排序:

unique():去重

去重算法,也要排序:

remove():删除指定数

splice():转移

splice()可以不同序列之间进行转移:

也可以同一序列内进行转移:

sort():排序

这里就有人要问了:为什么不用算法库的sort()?

发生了编译错误。

我们转向文件algorithm的第8403行,可以发现一点点问题产生的原因:

我们发现,算法库sort(),其底层使用的迭代器,支持减操作。

要认识这个问题,我们就要简单了解迭代器按照功能的分类。

3.1、迭代器分类的简单了解

迭代器按照功能,可以分为三类:

  • 单向:只能向后走一步(++)
  • 双向:可以向前、向后,但是只能一步(++、--)
  • 随机:可以向前、向后多步(++、--、+、-)

迭代器的种类,是由所属容器决定的:

我们可以理解为:List的迭代器,如果实现成随机迭代器,由于链表节点的空间不是连续的,那么需要遍历,代价变大。

而诸如vector之类容器的迭代器,由于顺序表空间是连续的,所以支持随机迭代器,代价就不那么大。

所以List的迭代器,实现成了代价更小的双向迭代器。

算法库里的方法,对传入的迭代器也有要求:


按照不同功能迭代器的说明,我们不难理解:双向迭代器可以被当作单向迭代器使用、随机迭代器可以被当作单向迭代器和双向迭代器使用。

而单向迭代器不可以被当作双向迭代器和随机迭代器使用,双向迭代器也不可以被当作随机迭代器使用。

也就是说,算法库的sort()不支持List的双向迭代器,所以开发者实现了支持双向迭代器的List成员sort()。

有时,我们也会看到这样的迭代器类型:

其实,迭代器还可以分为:

  • 只读(OutputIterator)
  • 只写(InputIterator)

现阶段我们只需要认为,只写迭代器,包括了单向、双向、随机迭代器。

3.2、List下的成员sort的性能讨论

首先我们要明白:

  • List下的成员sort,使用的是处理后的归并排序
  • 算法库中的sort,使用的是快速排序

这两个排序的时间复杂度,都是O(NlogN),但是有差异。

我们简单设计程序,印证这一点。

分别在List、vector中放入1000000个数据,然后记录、比较排序的用时:

cpp 复制代码
void test_op1()
{
	vector<int> v;
	list<int> l;

	const int n = 1000000;
	for (int i = 0; i < n; ++i)
	{
		int x = rand() % 100 + 1;
		v.push_back(x);
		l.push_back(x);
	}

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

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

我们当前是在x64的release版本下进行测试,而不是debug版本。因为debug版本编译、链接时要做很多处理,而release不需要。

我们可以发现,同样时间复杂度的排序方式,List的sort明显比算法库的sort慢了十几倍。

我们再进行一个测试:创建两个List,放入1000000个数据,其中一个List的数据被vector拷贝,vector经算法库sort排序,再拷贝回List;另一个List直接排序。

cpp 复制代码
void test_op2()
{
	list<int> l1;
	list<int> l2;

	const int n = 1000000;
	for (int i = 0; i < n; ++i)
	{
		int x = rand() % 100 + 1;
		l1.push_back(x);
		l2.push_back(x);
	}
	
	int begin1 = clock();
	vector<int> copy(l1.begin(), l1.end());
	sort(copy.begin(), copy.end());
	l2.assign(copy.begin(), copy.end());
	int end1 = clock();

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

	printf("list1:>%d\n", end1 - begin1);
	printf("list2:>%d\n", end2 - begin2);
}

可以看到,两种排序方式的用时几乎一致,有时拷贝并使用算法库的sort,还优于List的sort。

所以,List的sort,在大量访问数据的情况下,效率普遍低于算法库的sort。所以我们在大量访问数据的情况下,尽可能使用算法库的sort,其实就是借助vector等内存空间连续的容器。这可能是因为连续空间的访问效率比不连续空间的要快。

相关推荐
AC赳赳老秦1 小时前
采集行为合规自检:OpenClaw 自动校验 robots 协议与采集频率,规避违规采集风险
java·开发语言·c++·python·php·deepseek·openclaw
无名猿2 小时前
C++ 查找算法:std::find 与容器成员 find 的性能差距
c++·性能优化·stl·标准库
程序猿编码2 小时前
工业视觉多任务方案:可编程梯度信息视觉框架,训练评估 + C++ 部署完整实战
开发语言·c++·视觉·推理引擎
zhangrelay3 小时前
机械臂末端规划智能模型的误区演示
linux·笔记·学习·ubuntu·ros2
朝朝辞暮i4 小时前
C++ 第 21 课:struct —— 把一组相关数据打包在一起
开发语言·c++·算法
织码weavecodes4 小时前
培训平台部署备份与容灾:从三服务交付到可恢复运行
大数据·学习·开源软件
SHARK_pssm4 小时前
【C++——类和对象(下)】
开发语言·c++·经验分享·笔记
H.莓飛5 小时前
【数据结构】队列
linux·数据结构·centos
汉克老师5 小时前
GESP2026年9月认证C++二级( 第一部分选择题(1~7题)精讲
c++·gesp·小学生·学c++编程
沫璃染墨5 小时前
《从零入门Linux系统篇(五十四):线程篇·七——互斥锁底层原理:从原子交换到线程竞争与锁实现》
linux·运维·服务器·开发语言·c++·驱动开发·系统架构