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.

相关推荐
CaracalTiger1 小时前
HTTP 协议的基本概念(请求/响应流程、状态码、Header、方法)问题解决方案大全
开发语言·网络·python·深度学习·网络协议·http·pip
随缘而动,随遇而安1 小时前
第八十二篇 大数据开发基础:树形数据结构深度解析与实战指南(附创新生活案例)
大数据·开发语言·数据结构
西猫雷婶1 小时前
python学智能算法(十三)|机器学习朴素贝叶斯方法进阶-简单二元分类
开发语言·人工智能·python·深度学习·机器学习·矩阵·分类
武子康2 小时前
Java-49 深入浅出 Tomcat 手写 Tomcat 实现【02】HttpServlet Request RequestProcessor
java·开发语言·后端·学习·spring cloud·tomcat
张朝阳的博客2 小时前
哈夫曼树Python实现
开发语言·python
阑梦清川2 小时前
C#建立与数据库连接(版本问题的解决方案)踩坑总结
开发语言·数据库·c#
听风lighting3 小时前
1. C++ WebServer项目分享
linux·c语言·c++·设计模式·嵌入式·webserver
药9553 小时前
数据结构 4 (栈和队列)
java·开发语言·数据结构
smileNicky3 小时前
Java实现Excel图片URL筛选与大小检测
java·开发语言·excel
code_li3 小时前
C#实现语音预处理:降噪/静音检测/自动增益
开发语言·c#