当前位置: 首页 > news >正文

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),这样增加节点和删除节点的时间复杂度为O(1),而且有两种迭代器:正向迭代器和反向迭代器

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

3.成员函数

构造函数

空的构造

list<int> ls();

n个节点初始化相同的值

list<int> ls(5,3);

5个节点的值初始化为3

使用迭代器初始化

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());

运行结果:

拷贝构造

std::list<int> ls(v.begin() + 2, v.end());
std::list<int> _ls(ls);

头插和尾插

std::list<int> ls;
ls.push_front(2);//头插
ls.push_back(1);//尾插

迭代器的重要提醒

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

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

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

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

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

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

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

#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的结果

#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快很多,例如如下代码:

#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):

#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链表的节点全部剪切,之后头插到ls2ls2.splice(ls2.begin(), ls1);return 0;
}

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

运行结果:

single element(2):

#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):

#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系列

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)

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

 ,例如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.

相关文章:

  • 前端基础知识CSS系列 - 14(CSS提高性能的方法)
  • windows,java后端开发常用软件的下载,使用配置
  • Spring MVC参数绑定终极手册:单多参对象集合JSON文件上传精讲
  • Python 开发环境全栈隔离架构:从 Anaconda 到 PyCharm 的四级防护体系
  • mcp-server-chart Quickstart
  • Android中Native向System Service进行Binder通信的示例
  • Tensorflow推理时遇见PTX错误,安装CUDA及CuDNN, 解决问题!
  • C++实现手写strlen函数
  • 什么是池化
  • 图像特征检测算法ORB
  • C语言标准I/O库详解:文件操作与缓冲区机制
  • vscode设置代码字体
  • 【PX30 Qt 5.15 交叉编译环境搭建完整指南】
  • 动态规划:砝码称重(01背包-闫氏DP分析法)
  • SVN本地使用--管理个人仓库
  • CSS语法中的选择器与属性详解
  • vs code配置go开发环境以及问题解决 could not import cannot find package in GOROOT or GOPATH
  • Linux》》Shell脚本 基本语法
  • Apptrace如何帮我精准追踪移动广告效果?
  • Kernel K-means:让K-means在非线性空间“大显身手”
  • 做期货关注网站/湖北seo整站优化
  • c语言网站/湖南网站制作公司
  • 自己做的网站如何连接入数据库/做网站哪个公司最好
  • 网站建设的重要/网站seo外链建设
  • 昆明做网站优化/新闻发稿
  • 华侨城网站建设/关键词优化工具