😇 😇各位小伙伴们,大家好!我是bug。今天我们继续学习STL容器中的list:
(代码可能会有一点问题,请各位老铁指正 😘 😘 )
一、list的概念
🍉 🍉list:在C++中list容器是双向带头循环链表,这种结构在数据的插入删除上相比于vector有着极大的优势,其时间复杂度为O(1)。它唯一的缺点就是不支持随机访问,去查找数据只能够通过遍历的方式,而vector可以通过下标进行访问。
二、list的用法
🌹 🌹list的常用接口:
construct(构造函数) | 用法 |
---|---|
list() | 构造空list |
list (size_type n, const value_type& val = value_type()) | 构造包含n个数据为val的list |
list (const list& x) | 拷贝构造函数 |
list (InputIterator first, InputIterator last) | 用[first, last)区间中的元素构造list |
遍历操作 | 用法 |
---|---|
begin | 返回第一个元素的iterator |
end | 返回最后一个元素下一个位置的iterator |
rbegin | 返回第一个元素的reverse_iterator |
rend | 返回最后一个元素下一个位置的reverse_iterator |
操作空间/容量 | 用法 |
---|---|
empty | 检测list是否为空,是返回true,否则返回false |
size | 返回list中有效节点的个数 |
获得数据 | 用法 |
---|---|
front | 返回list的第一个节点中值的引用 |
back | 返回list的最后一个节点中值的引用 |
修改操作 | 用法 |
---|---|
push_front | 在list首元素前插入值为val的元素 |
pop_front | 删除list中第一个元素 |
push_back | 在list尾部插入值为val的元素 |
pop_back | 删除list中最后一个元素 |
insert | 在list position 位置中插入值为val的元素 |
erase | 删除list position位置的元素 |
swap | 交换两个list中的元素 |
clear | 清空list中的有效元素 |
🌳(1)list对象的创建
list创建对象的方式有很多,比如:
🌵 使用默认构造函数
🌵 使用带参构造函数
🌵 使用拷贝构造函数
🌵 使用赋值运算符重载
🌵 使用迭代器区间进行构造
🌵 创建匿名对象
🍒 🍒代码⬇️ ⬇️:
//无参构造
list<int> lt1;
//有参数构造
list<int> lt2(4, 5);
//迭代器区间进行构造
llist<int> lt3(lt2.begin(), lt2.end());
//拷贝构造
list<int> lt4(lt3);
//赋值运算符重载
list<int> lt5 = lt4;
🌳(2)list的遍历
与vector、string不同,list由于空间不连续,不支持用[]进行访问数据。所以list的遍历方式只有迭代器和范围for两种。
🍒 🍒代码⬇️ ⬇️:
list<int> lt1(4,5);
//迭代器进行遍历
list<int>::iterator it = lt1.begin();
while (it != lt1.end())
{
cout << *it << " ";
it++;
}
cout << endl;
//范围for进行遍历
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
🌳(3)迭代器失效问题****
在list中插入并不存在迭代器失效的问题,因为list的插入不会改变它原有的空间。但是list的删除会造成迭代器失效,而且失效的部分是指向了被删除的空间迭代器,其他部分的迭代器不会有所影响。
和vector一样,erase使用了返回新迭代器的方式来避免失效。
注意❗️ ❗️
迭代器有两种实现方式,具体应根据容器底层数据结构实现:
🍔1. 原生态指针,比如:vector的迭代器就是原生态指针
🍔 2. 将原生态指针进行封装,因迭代器使用形式与指针完全相同,因此在自定义的类中必须实现以下方法:
🍁1、指针解引用,重载operator*()
🍁 2、 指针->访问其所指空间成员,重载oprator->()
🍁3.、指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)至于operator–()/operator–(int)释放需要重载,根据具体的结构来抉择,双向链表可以以向前 移动,所以需要重载,如果是forward_list就不需要重载。
🍁4、 迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()
三、list的模拟实现
🍒 🍒这里我们来实现一个简易的vector⬇️ ⬇️:
//结点
_Tp _data;
_list_node<_Tp>* _next;
_list_node<_Tp>* _prev;
//构造函数
_list_node(const _Tp& val = _Tp())
//迭代器
typedef _list_node<_Tp> Link_type;
typedef _list_iterator<_Tp, Ref,Ptr> self;
//引用
typedef Ref reference;
typedef const Ref const_reference;
//指针
typedef Ptr pointer;
typedef const Ptr const_pointer;
//构造函数
_list_iterator(Link_type* pnode = nullptr)
_list_iterator(const self& lt)
//重载
reference operator*()
pointer operator->()
const_reference operator*()const
const_pointer operator->()const
bool operator!=(const self& x)const
bool operator==(const self& x)const
self& operator++()
self operator++(_Tp)
self& operator--()
self operator--(_Tp)
//list