首页 / 第十一章 · STL 标准模板库 / 11.3 list

11.3 list 双向链表

每个元素各住各的,靠指针互相连接——任意位置插入删除都是 O(1),但放弃了下标随机访问的能力。

本页目录

list 是一个双向链表。链表不像数组那样所有元素挤在一块连续的内存里,而是每个元素各住各的,彼此用"指针"串起来。这带来的好处是:在任意位置插入或删除元素都是 O(1),快到飞起。代价是:不能用下标直接跳到第 n 个元素,只能顺着链条一个一个找。使用前需引入 <list> 头文件。

list 的内存结构:每个节点都有自己的位置,靠箭头串联
head →
1
2
3
4
→ nullptr
每个节点散落在内存的不同位置(不连续),通过指针互相连接。在中间插入新节点只需改两个指针,不用搬动其它元素。
⚠️
注意:
· list 不支持随机访问,不能像 vector 那样用 l[0]l.at(0) 访问元素,必须通过迭代器遍历。
· list 支持 O(1) 的头部插入/删除(push_front / pop_front),这是 vector 不具备的。
11.3.1 定义与初始化
C++ · list 的几种初始化方式
1#include <iostream>
2#include <list> // list 位于此头文件中
3using namespace std;
4
5int main()
6{
7 list<int> l1; // 空链表
8 list<int> l2(5); // 5 个元素,全部初始化为 0
9 list<int> l3(5, 7); // 5 个元素,全部初始化为 7
10 list<int> l4 = {1, 2, 3, 4, 5}; // 用花括号初始化
11 list<int> l5(l4); // 拷贝构造
12 return 0;
13}
11.3.2 常用操作

🔍 元素访问

C++ · list 的元素访问
1list<int> l = {10, 20, 30};
2int first = l.front(); // 10——第一个元素
3int last = l.back(); // 30——最后一个元素
4// ❌ l[0] —— 编译错误!list 不支持下标访问

✏️ 增删修改

C++ · push_front / push_back / insert / erase
1list<int> l = {2, 3, 4};
2l.push_front(1); // {1, 2, 3, 4}——头部插入
3l.push_back(5); // {1, 2, 3, 4, 5}——尾部插入
4l.pop_front(); // {2, 3, 4, 5}——头部删除
5l.pop_back(); // {2, 3, 4}——尾部删除
6
7auto it = l.begin();
8++it; // it 指向第二个元素(3)
9l.insert(it, 99); // 在 it 前面插入 99:{2, 99, 3, 4}
10l.erase(it); // 删除 it 指向的元素:{2, 99, 4}
11l1.swap(l2); // 交换两个链表的内容

🔄 迭代器遍历

C++ · list 的遍历方式
1list<int> l = {1, 2, 3};
2// 用迭代器遍历(list 的迭代器只支持 ++ 和 --,不支持 it + 3 这种跳转)
3for (auto it = l.begin(); it != l.end(); ++it)
4{
5 cout << *it << " "; // 输出:1 2 3
6}
7
8// 范围 for 更简洁
9for (int x : l) {
10 cout << x << " "; // 输出:1 2 3
11}
11.3.3 完整使用示例
C++ · list 综合示例
1#include <iostream>
2#include <list>
3using namespace std;
4
5int main()
6{
7 list<int> l;
8
9 // 在头部和尾部随意插入
10 l.push_back(2);
11 l.push_front(1);
12 l.push_back(3);
13 // l = {1, 2, 3}
14
15 cout << "链表内容: ";
16 for (int x : l) {
17 cout << x << " "; // 输出:1 2 3
18 }
19 cout << endl;
20
21 // 删除中间的偶数
22 for (auto it = l.begin(); it != l.end(); ) {
23 if (*it % 2 == 0)
24 it = l.erase(it); // list 的 erase 也返回下一个有效迭代器
25 else
26 ++it;
27 }
28 // l = {1, 3}
29
30 cout << "首元素: " << l.front() << endl; // 输出:1
31 cout << "尾元素: " << l.back() << endl; // 输出:3
32 cout << "大小: " << l.size() << endl; // 输出:2
33
34 return 0;
35}
📋 list 常用操作速查表

把本节出现过的方法按用途归类汇总,写代码时可以直接当参考卡用。

方法分类作用
l.size() / l.empty()容量元素个数 / 判断是否为空
l.clear()容量清空所有元素
l.front() / l.back()访问第一个 / 最后一个元素
l.push_front(x) / l.pop_front()修改头部插入 / 删除,O(1)(vector 没有)
l.push_back(x) / l.pop_back()修改尾部插入 / 删除,O(1)
l.insert(it, x)修改在 it 前插入元素,O(1)
l.erase(it)修改删除 it 指向的元素,返回下一个有效迭代器
l1.swap(l2)修改交换两个链表的全部内容
l.begin() / l.end()遍历正向迭代器范围(只支持 ++ / --,不支持跳转)
🎯
vector vs list 怎么选?需要下标随机访问、主要在尾部增删 → 用 vector;需要频繁在头部或中间插入删除、不在乎随机访问 → 用 list。两者都需要的场景,下一节的 deque 是折中方案。