在了解list容器之前,我们要知道双向链表是什么,不知道的可以看这一篇:数据结构之链表-CSDN博客,其中就讲了双向链表。
list
下面我会从list结构简单讲解,list的相关函数介绍及使用,list的简单实现来讲解list。
list的结构
list是封装的双向链表结构,因为独特的链式结构,所以在插入、删除效率上很高。具体结构分析看上面的博客有具体讲解。
list的函数
构造函数
构造函数里面牵扯了内存池的东西,这里我先不把它扯进来防止难以理解。后面单独出一期再讲(其实是我还没学内存池)
在每一个构造函数构造数据节点之前,会有一次同样的操作,就是构造header哨兵位节点。方便数据插入删除的操作。
默认构造函数
默认构造就是不构造数据节点,只是创建哨兵位节点。
带参构造
将这两种传参统一一下就是:
第一个n表示链表的长度,第二个表示链表每个节点的内容。
迭代器构造
不知道迭代器的可以看这一篇博客c++迭代器的介绍-CSDN博客
将一个容器的迭代器输入进去进行构造:
拷贝构造
拷贝构造就是用一个已经创建好的对象的值去创建一个新的对象:
初始化设定项列表构造
还不知道初始化设定项列表的,可以去看看这一篇博客:c++ 初始值设定项列表(initializer_list)-CSDN博客
右值引用构造
这个我先不讲,还没仔细的学。
析构函数
析构就是要将每一个节点包括哨兵位的内存释放掉。
operator=
operator=就是赋值操作,与拷贝不同在于赋值是给已经创建好的对象赋值。
迭代器相关函数
迭代器相关函数所有容器都是一样的,推荐大家直接去看这篇博客,看完后所有容器的迭代器函数都没问题了:c++迭代器的介绍-CSDN博客
capacity相关函数
empty函数就是判断是否为空,是返回true,反之返回false。
size函数返回链表的长度
max_size一般是返回size_t形式的-1(理论上我们的链表是可以十分长的,我们的堆区内存很大,但是我们size_t变量的返回最大就-1,这里为什么是-1要有点补码原码的知识才行)
element相关函数
front函数就是返回开头第一个节点的值的引用,让我们可以读取值,在非const的情况下我们还可以直接改变它的值。
back和front函数是一样的,只是返回的末尾引用
modify相关的函数
这些函数我们在上一个容器vactor里面基本都见过了,所以很好掌握:
第一个assign函数就是重新给链表值从而覆盖之前的内容:
这里支持的重赋值有迭代器、带参构造和初始化设定项列表三种。
第二个emplace_front 和第三个push_front在功能上面是相同的,都是头插,但是前者在某一些情况下效率更高,这里不多介绍,我还没学。
pop_front就是头删
emplace_back 和push_back也是同样的作用,用于尾插。
pop_back是用于尾插
emplace 和insert也是同理的,都是属于插入的
这里只讲insert:
这里就可以看成在某个节点前面使用了构造函数插入这些值。那么使用就和构造函数没什么不同,除了没有默认,毕竟默认不构造值。
erase就是删除节点或者区间
这里区间只能用迭代器来表示。
resize函数可以重新定义长度
如果是比原来短就直接删除多余的,如果是增长就是输入一个缺省值,不输入就是T()来进行构造。
clear函数就是清空链表,只留哨兵节点
operations相关函数
这里面有些函数是在list里面独有的
splice(连接),就是将两个链表链接在一起,但是有主体,是A链接给B函数B链接给A,假如B链接给A,那么B的相关内容就会给A,B就没有没有这些内容了。
这里看着有六个函数,其实可以简化成三个,不同的就是右值引用。所有三大块不同就在于后面的迭代器器,如果后面没有跟迭代器那么就是把x链表的所有内容连接过去,如果有一个迭代器,那么就是把这个地方的一个节点连接过去,如果是迭代器区间,那么就是将迭代器区间的东西连接过去。
remove就是查找val,如果和val相等的节点值就删除,有几个删几个。
remove_if函数
就是有条件的删除,删除的内容更宽泛:
这里我们可以传回调函数地址,或者传仿函数(不知道仿函数的可以看这一篇:C++ 仿函数-CSDN博客)
例如下面:
unique
unique就是去重的操作,但是前提为这个链表是有序的。
merge
merge就是进行归并操作,将两个链表合成一个按相关要求排序的链表。既然是归并那么就要保证它是有序的。
sort
这个不用多说就是用归并来实现的排序,速度比std::sort要慢三四倍。
reverse
反转链表,不用多说
observer类函数
这个是和内存池相关的函数,内存池我单独出一篇讲(其实是还没学)
全局函数
支持逻辑运算操作
swap
这个swap比std::swap更加比配,所以在调用全局的swap时会优先匹配这个函数,然后实例化。防止使用std::swap出错。
list的实现
list主要的难点就是框架的搭建,几个函数的实现都很简单。所以我主要讲框架。
节点类封装
首先是双向链表的节点,因为list类会经常访问,所以我们可以将节点类用struct来封装:
迭代器封装
我们list的迭代器就不同于vector、string,因为list的内存不是连续的,所以不能像vector、string直接迭代器++或者--,需要用类封装iterator并用operator重载。
另外,我们的迭代器分为iterator和const_iterator等迭代器,那么我们是否要写两个类来分别实现呢?其实我们只要用类模版就行了:
list类封装
那些相关函数我就不实现了,都不是特别难。