CD45.【C++ Dev】STL库的list的使用

目录

1.知识回顾

2.cplusplus官网对list的介绍

3.成员函数

构造函数

空的构造

n个节点初始化相同的值

使用迭代器初始化

拷贝构造

头插和尾插

迭代器的重要提醒

排序函数

归并函数

去重函数

转移链表(剪切+粘贴)

4.迭代器的分类

​编辑附:5种迭代器的简要说明


1.知识回顾

之前在C++Contest专栏中简单讲过list的的使用,由于在竞赛中不常使用,因此没有细讲,可以简单回顾:

CC31.【C++ Cont】静态实现单链表

CC32.【C++ Cont】静态实现双向链表及STL库的list

2.cplusplus官网对list的介绍

可以得知:list的底层结构是双向链表(doubly-linked lists ),这样增加节点和删除节点的时间复杂度为,而且有两种迭代器:正向迭代器和反向迭代器

而forward_list的底层结构是单向链表(single-linked lists)

3.成员函数

构造函数

空的构造

cpp 复制代码
list<int> ls();

n个节点初始化相同的值

cpp 复制代码
list<int> ls(5,3);

5个节点的值初始化为3

使用迭代器初始化

cpp 复制代码
std::vector<int> v;
v.push_back(5);
v.push_back(4);
v.push_back(3);
v.push_back(2);
v.push_back(1);
std::list<int> ls(v.begin() + 2, v.end());

运行结果:

拷贝构造

cpp 复制代码
std::list<int> ls(v.begin() + 2, v.end());
std::list<int> _ls(ls);

头插和尾插

cpp 复制代码
std::list<int> ls;
ls.push_front(2);//头插
ls.push_back(1);//尾插

迭代器的重要提醒

1.因为list的存储空间不是连续的,因此它是没有像"iterator+n"形式的

2.因为list的存储空间不是连续的,因此****list的迭代器比较没有>和<,只有!=

例如使用迭代器来遍历list容器:

cpp 复制代码
auto it = ls.begin();//正向迭代器
while (it != ls.end())
	std::cout << *it << std::endl;

而且algorithm中的find函数也使用了operator!=,并没有采用operator<或operator>,为了保持通用性(容器的存储空间可能连续,也有可能不连续)

3.list的insert没有迭代器失效,因为无扩容或野指针的问题;但erase是删除节点,有迭代器失效的问题

解释使用erase会导致迭代器失效,例如以下代码:

cpp 复制代码
#include <iostream>
#include <list>
#include <algorithm>
int main()
{
	std::list<int> ls;
	ls.push_front(1);//头插
	ls.push_back(2);//尾插
	ls.push_back(3);//尾插
	auto it = find(ls.begin(), ls.end(), 2);
	ls.erase(it);
	*it = 3;
	return 0;
}

运行结果:因为it指向的空间被释放,因此不能对it指向的空间做出修改

为了应对迭代器失效的问题,C++标准定义了erase函数的返回值为迭代器

返回的迭代器指向被删除元素的下一个元素

下断点到return 0,监视窗口查看ret的结果

cpp 复制代码
#include <iostream>
#include <list>
#include <algorithm>
int main()
{
	std::list<int> ls;
	ls.push_front(1);//头插
	ls.push_back(2);//尾插
	ls.push_back(3);//尾插
	auto it = find(ls.begin(), ls.end(), 2);
	auto ret = ls.erase(it);
	return 0;
}

排序函数

除了algorithm中提供了sort函数,其实list中也提供了

但对于数量较大的数据,使用vector排序其实要比list快很多,例如如下代码:

cpp 复制代码
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <limits.h>
#include <time.h>
using namespace std;
int main()
{
	list<int> ls;
	vector<int> v;
	srand((unsigned int)time(NULL));
	for (int i = 0; i < 1000000; i++)
	{
        //保证list和vector中需要排序的数字都一样,控制变量
		int random= rand() % INT_MAX;
		ls.push_back(random);
		v.push_back(random);
	}

	clock_t begin1 = clock();
	sort(v.begin(), v.end());
	clock_t end1 = clock();
	clock_t begin2 = clock();
	ls.sort();
	clock_t end2 = clock();
	cout << "vector使用" << end1 - begin1 << "clock ticks" << endl;
	cout << "list使用" << end2 - begin2 << "clock ticks " << endl;
	return 0;
}

Release模式下,运行结果:

归并函数

去重函数

unique adj.独一无二的

建议:先排序后去重,效率高

转移链表(剪切+粘贴)

entire list(1):

cpp 复制代码
#include <iostream>
#include <list>
using namespace std;
int main()
{
	list<int> ls1;
	list<int> ls2;
	ls1.push_back(1);
	ls1.push_back(2);
	ls1.push_back(3);
	ls2.push_back(4);
	ls2.push_back(5);
	ls2.push_back(6);
    //将ls1链表的节点全部剪切,之后头插到ls2
	ls2.splice(ls2.begin(), ls1);
	return 0;
}

注:区间不能重叠,否则死循环

运行结果:

single element(2):

cpp 复制代码
#include <iostream>
#include <list>
using namespace std;
int main()
{
	list<int> ls1;
	list<int> ls2;
	ls1.push_back(1);
	ls1.push_back(2);
	ls1.push_back(3);	
	ls2.push_back(4);
	ls2.push_back(5);
	ls2.push_back(6);
    //将ls1的头节点剪切下来,之后粘贴到ls2的尾端
	ls2.splice(ls2.end(), ls1, ls1.begin());
	return 0;
}

运行结果:

element range(3):

cpp 复制代码
#include <iostream>
#include <list>
using namespace std;
int main()
{
	list<int> ls1;
	list<int> ls2;
	ls1.push_back(1);
	ls1.push_back(2);
	ls1.push_back(3);	
	ls1.push_back(8);	
	ls2.push_back(4);
	ls2.push_back(5);
	ls2.push_back(6);
	auto begin = find(ls1.begin(), ls1.end(), 2);
	auto end = find(ls1.begin(), ls1.end(), 8);
	//将ls1的一部分节点剪切下来,区间是[begin,end),之后粘贴到ls2的尾端
	ls2.splice(ls2.end(), ls1, begin, end);
	return 0;
}

运行结果:

4.迭代器的分类

这里只提3种,剩下的两种之后的文章再提

1.正向(单向)迭代器(Forward Iterator)

只能使用operator++、operator==和operator!=这几个运算符

例如forward_list和unordered_xxx系列

cpp 复制代码
std::forward_list<int> fls;
auto it = fls.begin();
it++;//正确
it--;//错误
return 0;

unordered_set底层使用了双向链表,因此可以使用operator++,不违反标准

2.双向迭代器(Bidirectional Iterator)

可以使用operator++和operator--,例如list、map和set

例如algorithm中的reverse函数只能传双向迭代器

3.随机访问迭代器(Random Access Iterator)

对于随机访问迭代器来说,指向的元素位于一段连续的存储空间中,因此访问元素时间复杂度为

,例如vector、string和deque,可以使用operator++和operator--,甚至是operator+和operator-

例如sort如果要传迭代器,只能使用随机访问迭代器

附:5种迭代器的简要说明

以下摘自Iterators | Microsoft Learn

There are five categories of iterators. In order of increasing power, the categories are:

  • Output . An output iterator X can iterate forward over a sequence by using the ++ operator, and can write an element only once, by using the * operator.

  • Input . An input iterator X can iterate forward over a sequence by using the ++ operator, and can read an element any number of times by using the * operator. You can compare input iterators by using the == and != operators. After you increment any copy of an input iterator, none of the other copies can safely be compared, dereferenced, or incremented afterwards.

  • Forward . A forward iterator X can iterate forward over a sequence using the ++ operator and can read any element or write non-const elements any number of times by using the * operator. You can access element members by using the -> operator and compare forward iterators by using the == and != operators. You can make multiple copies of a forward iterator, each of which can be dereferenced and incremented independently. A forward iterator that is initialized without reference to any container is called a null forward iterator. Null forward iterators always compare equal.

  • Bidirectional . A bidirectional iterator X can take the place of a forward iterator. You can, however, also decrement a bidirectional iterator, as in --X, X--, or (V = *X--). You can access element members and compare bidirectional iterators in the same way as forward iterators.

  • Random access . A random-access iterator X can take the place of a bidirectional iterator. With a random access iterator, you can use the subscript operator [] to access elements. You can use the +, -, += and -= operators to move forward or backward a specified number of elements and to calculate the distance between iterators. You can compare bidirectional iterators by using ==, !=, <, >, <=, and >=.

All iterators can be assigned or copied. They're assumed to be lightweight objects and are often passed and returned by value, not by reference. Note also that none of the operations previously described can throw an exception when performed on a valid iterator.

相关推荐
Trouvaille ~4 分钟前
【Linux】TCP Socket编程实战(一):API详解与单连接Echo Server
linux·运维·服务器·网络·c++·tcp/ip·socket
喜欢喝果茶.13 分钟前
QOverload<参数列表>::of(&函数名)信号槽
开发语言·qt
亓才孓14 分钟前
[Class类的应用]反射的理解
开发语言·python
努力学编程呀(๑•ี_เ•ี๑)14 分钟前
【在 IntelliJ IDEA 中切换项目 JDK 版本】
java·开发语言·intellij-idea
坚果派·白晓明16 分钟前
在鸿蒙设备上快速验证由lycium工具快速交叉编译的C/C++三方库
c语言·c++·harmonyos·鸿蒙·编程语言·openharmony·三方库
小镇敲码人23 分钟前
深入剖析华为CANN框架下的Ops-CV仓库:从入门到实战指南
c++·python·华为·cann
island131436 分钟前
CANN GE(图引擎)深度解析:计算图优化管线、内存静态规划与异构任务的 Stream 调度机制
开发语言·人工智能·深度学习·神经网络
坚持就完事了40 分钟前
Java中的集合
java·开发语言
魔芋红茶44 分钟前
Python 项目版本控制
开发语言·python
云小逸1 小时前
【nmap源码解析】Nmap OS识别核心模块深度解析:osscan2.cc源码剖析(1)
开发语言·网络·学习·nmap