接下来开始第三个SLT容器,list容器的学习

参考网站:https://legacy.cplusplus.com/reference/list/list/?kw=list

目录

一、list容器介绍:

二、list容器的接口:

1.构造函数和拷贝构造list

2.析构函数~list

3.operator=

4.迭代器iterator

5.Capacity

6.元素访问

7.头插头删

8.insert

9.erase

10.swap、resize、clear

11.拼接splice

12.remove

13.sort

14.unique

15.合并merge

总结:


一、list容器介绍:

list容器也是一个模板容器,支持多种数据类型使用,实现的功能是双向链表。

使用list需要导入头文件#include<list>

二、list容器的接口:

1.构造函数和拷贝构造list

  • 首先是一个全缺省构造,缺省参数是用于自定义内存池,基本不会用到,所以当成不传参即可
  • 第二是用n个val值来进行初始化
  • 第三个是用一段迭代器区间进行初始化,可以是同类型容器的,也可以是不同类型的,如vector容器的迭代器,编译器会自动转化,但要保证数据可转化,如list<int>,那么vector也要是int类型
  • 第四个是拷贝构造,使用上不需要特别关注,反正都是自动调用

示例代码:

注:编译器在监视处做了特殊处理,用数组模拟的形式,让我们可以直接看到链表的结构。

而通过监视窗口我们可以看到lt1到lt5都成功初始化了

2.析构函数~list

用于清理对象申请的资源,了解一下即可

3.operator=

重载=,如果被赋值的对象已经存在就进行赋值,如果不存在则调用拷贝构造,新建一个对象。

4.迭代器iterator

begin、end这些函数返回相应的迭代器的值,用于遍历

在这里补充一点迭代器的重要知识:

  • 首先,之前在使用string和vector的时候底层都是数组,所以可以通过相似的for循环实现遍历,而list是链表,显然不能简单的++来实现遍历了,并且后面还会有更复杂的容器结构,如果都是手动实现的话太过麻烦。另外,要手动遍历还需要对底层结构有清晰的了解,才能写出正确的遍历
  • 而迭代器提供了一套统一接口,适用于所有容器。你还不需要知道底层就能直接上手使用,这体现了迭代器使用的封装思想

  • 第二,迭代器其实有三种类型,单向迭代器,双向迭代器,随机迭代器
  • 单向迭代器只能单向访问,只支持++,即一次只能+1,如:单向链表
  • 双向迭代器可以双向访问,支持++和--,即一次只能+1或-1,如:双向链表list
  • 随机迭代器可以访问任意位置访问, 支持++、--、+、-,即一次可以+n或-n,如:string和vector
  • 所以对于一些情景下,迭代器类型不同也是不能使用的,如std::sort要求是随机迭代器,所以list对象就不能调用std::sort函数
  • 随机迭代器又能看成一种特殊的双向迭代器,双向迭代器能看成特殊的单向迭代器

5.Capacity

Capacity的部分不需要过多介绍,empty判空,size返回链表大小。而由于链表不是由数组实现的,所以这部分接口会比较少

6.元素访问

返回头元素和尾元素的引用

7.头插头删

push_front和pop_front是头插头删,push_back和pop_back是尾插尾删。

而emplace_front和emplace_back可以看成是一种特殊的头插尾插。

8.insert

insert是在指定位置之前的位置插入,第一个参数是一个迭代器,插入有三种方式:

1. 插入一个数据(一个结点)

2. 插入n个数据(n个结点)

3. 一段迭代器区间插入(多个结点)

注意:

1. 与vector不同,由于list是通过多个结点实现的,插入时只是改变了结点的链接关系,而没有改变原结点的地址,所以不会有迭代器失效问题。

2. 由于list不是随机迭代器,所以不能快速访问到具体哪个结点,除了头尾结点外,需要先遍历到需要插入的位置再调用insert函数。

9.erase

erase是删除指定位置之后的位置,可以删一个结点,也可以删一段迭代器区间。

erase使用时会有迭代器失效问题,因为删除后结点会释放,若想继续使用可以用erase的返回值进行重新赋值。

10.swap、resize、clear

swap用于交换数据

resize用于重定义链表大小,如果n小于当前大小,则将其缩短,如果给定的n大于当前大小,则填充至n大小,默认用默认构造出来的数据填充

clear用于清空链表

11.拼接splice

splice可以将数据剪切再粘贴到指定位置之前,可以方便地移动数据,有三种方式

1. 拼接一个list链表的全部结点

2. 拼接一个list链表的一个结点i

3. 拼接一个list链表的一段迭代器区间

12.remove

移除链表中所有值等于val的结点

13.sort

由于std::sort不能完成链表的排序,所以list还单独实现了一个sort接口,用于将链表排序,底层是通过归并排序实现。

不过这个list::sort一般在数据量不是很大的时候使用,如果数据量较大通常会采用先将数据拷贝到vector容器,再用std::排序,最后拷贝回list链表的形式,可以提高效率。原因是链表在内存中不是连续存储,直接用链表排序会耗费更多的时间。

示例代码:

// 拷贝vector
vector<int> v(lt1.begin(), lt1.end());
// 进行排序
sort(v.begin(), v.end());
// 拷贝回lt2
lt2.assign(v.begin(), v.end());

14.unique

删除链表中所有重复的值,但是前提是链表有序,可以用sort来对链表排序

15.合并merge

merge的作用是将一个链表x的所有数据合并到另一个链表中。

但前提是两个链表已经有序,合并完的链表也会保持有序,而链表x会清空。

总结:

本篇文章介绍了list容器,并学习了它的各个接口的使用方法,尤其可以关注一下后面几个函数,是之前学习string和vector时没有接触过的。

以上便是本篇文章的所有内容了,如果觉得有帮助的话可以点赞收藏加关注支持一下!

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐