LIST
- List 基本接口介绍
- 前言
- list 构造方法
- list 析构方法
- 容量相关
- 元素获取
- 迭代器
- 元素的修改
- 其他相关操作
前边博客中已经介绍了c++ STL 中的 string 以及 vector 基本接口的使用方法并进行了接口的模拟实现,接下来让我们来学习 list 的基本接口使用方法吧~~
List 基本接口介绍
前言
List 是一个带头结点的双向循环链表,已知头节点 _head 可以找到它的前驱节点(尾节点)以及后继节点(首节点–第一个节点),但是寻找中间的某个节点信息时候就需要从 _head 位置开始进行遍历:
list 构造方法
本博客介绍的是 C++98 下的构造方法:
(1)构造一个空链表
list (const allocator_type& alloc = allocator_type());
(2)构造有 n 个值为 val 的链表
list (size_type n, const value_type& val = value_type(),
const allocator_type& alloc = allocator_type());
(3)区间构造方法
template < class InputIterator >
list (InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
(4)拷贝构造
list (const list& x);
为了便于后续的接口测试,我们来自定义一个打印函数:
template<class T>
void PrintMyList(const list<T>& L)
{
for (auto e : L)
cout << e << " ";
cout << endl;
}
接下来看看如何进行使用这些构造接口:
void my_test0()
{
list<int> L1;
list<int> L2(5, 3);
vector<int> v{ 1,2,3,4,5,6,7,8 };
list<int> L3(v.begin(), v.end());
list<int> L4(L3);
}
list 析构方法
在使用库中的接口信息,链表使用结束是会自动进行销毁操作的。
容量相关
元素获取
迭代器
(1)正向迭代器
回顾在 vector 中,我们提到迭代器是为了便于获取容器中间的所有元素,因此在正向迭代器中 begin() 指向的是第一个元素的位置,end() 指向的是最后一个有效元素的下一个空间的位置
而在 list 当中,由于 list 是带头双向循环链表,因此将 begin() 设在首节点的位置 ,end() 设在最后一个有效元素的下一个空间的位置,也就是头节点的位置:
(2)反向迭代器
与正向迭代器刚好相反:
我们来测试一下遍历 list 链表:
元素的修改
(1)尾插尾删、头插头删接口测试
(2)任意位置插入接口测试
insert
在 pos 位置插入单个值为 val 的元素:
iterator insert (iterator position, const value_type& val);
1)insert 在第一个元素之前插入新元素:
从图中我们可以看出在链表的头部插入元素可以成功,并且打印链表信息也是可以的
回顾
在 vector 容器中我们提到了“迭代器失效”,是指由于插入元素或是删除元素导致容器底层空间发生变化而导致迭代器所指向的位置失效,那么在 list 中是否也会发生迭代器失效呢?我们可以打印迭代器所指向位置的元素来试试看:
从打印信息来看,在 pos 位置之前插入新的元素时,并未发生迭代器的失效
2)insert 在尾部插入新元素:
3)insert 在指定元素之前插入新元素:
在此段代码中我们使用到了查找算法,需要对 list 链表进行从头开始的遍历:
int val=4; //要进行查找的元素
auto pos = L1.begin();
while (pos != L1.end())
{
if (*pos == val) //找到该元素位置
break;
++pos;
}
假如代码中存在多处的查找操作,采用普通的遍历操作会使得代码效率极大地降低,并且会造成代码的冗余,因此在 C++ 库中给我们提供了一个全局的查找接口 find,可以在 vector 容器或是 list 链表中进行查找操作:
template <class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val);
因此上述查找我们直接可以使用一行代码来进行替换:
auto pos = find(L1.begin(), L1.end(), 4);
在 pos 位置插入 n 个值为 val 的元素:
void insert (iterator position, size_type n, const value_type& val);
在 pos 位置插入某个给定区间中的元素
template < class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);
(3)在任意位置删除接口测试
删除某个位置元素:
iterator erase (iterator position);
可以发现,删除元素时候导致了删除位置的迭代器失效了,但是后续的元素可以正常进行打印,在上一篇博客中我们指导在 vector 容器中插入或删除元素都会导致迭代器失效,并且操作(插入或删除)之后不能够获取到后续元素,而 list 只有在删除元素时候会导致迭代器的失效
为防止发生迭代器失效,我们需要将删除之后的元素位置进行返回即可避免:
删除区间中元素:
iterator erase (iterator first, iterator last);
(4)swap 交换接口测试
list<int> L1{ 1,2,3,4,5,6 };
list<int> L2{ 9,8,7,6,5 };
L1.swap(L2);
(5)resize 接口测试
resize 修改有效元素个数,多余空间使用 val 来进行填充:
void resize (size_type n, value_type val = value_type());
(6)clear 接口测试
清空所有有效元素:
其他相关操作
(1)remove 移除所有值为 val 的元素
void remove (const value_type& val);
(2)remove_if 移除满足条件的元素
template < class Predicate>
void remove_if (Predicate pred);
(3)sort 对 list 中元素进行排序
sort 默认是按照升序进行排序的:
若想要指定排序方式,我们可以自定义一个函数:
//定义排序方式函数
bool My_Compare(int left, int right)
{
//return left < right; // 小于方式---------升序排序
return left > right; //大于方式----------降序排序
}
(4)merge 合并两个链表
默认方式进行合并
void merge (list& x);
当我们调用 merge 接口时候发现触发了异常,这主要是因为编译器不知道以何种方式对这两个链表进行合并,因此在进行链表合并操作之前我们要先对链表进行一个排序的操作:
自定义合并链表的方式(降序为例)
template < class Compare>
void merge (list& x, Compare comp);
//定义排序方式函数
bool My_Compare(int left, int right)
{
//return left < right; // 小于方式---------升序排序
return left > right; //大于方式----------降序排序
}
(5)reverse 链表的逆置
直接调用接口即可:
有关于 list 库中接口的测试就到这里啦,小伙伴们一定要自己动手来测一测才能理解得更加深刻哦!
下一节我们来介绍有关于 list 相关接口的模拟实现哈!(提示:注意理解迭代器的使用内涵**)
敲黑板:
多练多理解,多试错多调试才能够进步,加油吧!