0. 官方文档
https://cplusplus.com/r eference/list/list/?kw=list
1. list 介绍
list 就是为了解决 vector 的缺陷出现的。
vector 缺陷:
-
头部和中部的插入删除效率低。O(N),因为需要挪动数据。
-
插入数据空间不够需要增容,增容需要开新空间,拷贝数据,释放旧空间,会付出很大代价。
list 优点:
-
list 头部、中间插入不再需要挪动数据,效率高。O(1)
-
list 插入数据是新增节点,不需要扩容。
list 缺点:
- 不支持随机访问。
所以实际使用中 vector 和 list 是相辅相成的两个容器。
2. list 的迭代器
从支持的操作接口的角度分 迭代器 的类型:
-
单向(forward_list)
-
双向(list)
-
随机(vector)
从使用的场景的角度分 迭代器 的类型:
-
正向迭代器
-
反向迭代器
-
const迭代器

【注意】
-
begin与end为正向 迭代器 ,对迭代器执行++操作,迭代器向后移动
-
rbegin与rend为反向 迭代器 ,对迭代器执行++操作,迭代器向前移动
迭代器的使用
正向、反向、const 迭代器
cpp
/* 迭代器 */
void test_list1()
{
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
lt1.push_front(0);
lt1.push_front(-1);
lt1.push_front(-2);
lt1.push_front(-3);
// 正向普通迭代器遍历
list<int>::iterator it1 = lt1.begin();
while (it1 != lt1.end())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
list<int> lt2(lt1); // 拷贝构造
print_list(lt2); // const 迭代器
list<int> lt3;
lt3.push_back(10);
lt3.push_back(20);
lt3.push_back(30);
lt3.push_back(40);
lt1 = lt3;
// 只要一个容器支持迭代器,旧可以使用范围for的操作
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
// 反向迭代器
list<int>::reverse_iterator rit = lt1.rbegin();
while (rit != lt1.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
}
头插头删,尾插尾删
cpp
/* 头插头删,尾插尾删 */
void test_list2()
{
// 头插与尾插
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_front(0);
lt1.push_front(-1);
print_list(lt1);
// 尾删
lt1.pop_back();
// 头删
lt1.pop_front();
print_list(lt1);
}
任意位置的插入删除
cpp
/* 任意位置的插入删除 */
void test_list3()
{
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
print_list(lt1);
// find 函数的查找范围是左闭右开的,[first, last)
list<int>::iterator pos = find(lt1.begin(), lt1.end(), 3);
if (pos != lt1.end()) // 一定要判断,不然找不到会直接尾插
{
lt1.insert(pos, 30); // 在 3 的前面插入
// 与vector不同,这里的 insert 并不会使 pos 迭代器失效
lt1.erase(pos); // 删除 3
}
print_list(lt1);
}
链表排序(不实用,不推荐)
cpp
/* 链表排序(不实用,不建议) */
void test_list4()
{
list<int> lt1;
lt1.push_back(4);
lt1.push_back(2);
lt1.push_back(5);
lt1.push_back(1);
lt1.sort(); // 排序
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
lt1.reverse(); // 逆置
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
}
erase 导致迭代器失效问题
cpp
void test_list5()
{
list<int> lt1;
lt1.push_back(4);
lt1.push_back(2);
lt1.push_back(5);
lt1.push_back(1);
lt1.push_back(6);
lt1.push_back(3);
// 删除其中所有偶数
list<int>::iterator it = lt1.begin();
while (it != lt1.end())
{
if (*it % 2 == 0)
{
lt1.erase(it); // 删除后 it 迭代器失效
}
// 无法++,会报错无法进行递加操作
++it;
}
print_list(lt1);
}
修改一下关键代码:
cpp
// 删除其中所有偶数
list<int>::iterator it = lt1.begin();
while (it != lt1.end())
{
if (*it % 2 == 0)
{
it = lt1.erase(it);
}
else
{
++it;
}
}
迭代器源码实现(简化版本)
下图中上半部分是 vector 的迭代器,下半部分是 list 的迭代器。

vector 是一段连续的空间,所以可以直接把迭代器定义为类型的指针:
cpp
typedef T value_type;
typedef value_type* iterator;
当我们 ++iterator 时,迭代器可以直接往下走到下一个元素。
但是链表是非连续空间,是由一个个节点链接而成的,如果我们需要++迭代器来获得链表中的下一个元素,就不能简单把迭代器定义为类型的指针。要解决这个问题,我们可以用一个类型去封装节点的指针构成一个自定义类型,然后重载*、++等运算符,就可以达到我们想要的效果。