102 std::list (双向链表容器)


作者Lou Xiao, deepseek创建时间2025-04-02 17:20:19更新时间2025-04-02 17:20:19

1. 基本概念

std::list 是 C++ 标准模板库(STL)中的一个双向链表容器。

1.1 主要特性

  • 双向链表结构:每个元素包含指向前驱和后继的指针
  • 非连续存储:元素在内存中不连续存储
  • 高效插入/删除:在任何位置插入或删除元素的时间复杂度为 O(1)
  • 随机访问效率低:访问特定位置的元素需要 O(n) 时间
  • 动态大小:可根据需要自动调整大小

1.2 头文件

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <list>

2. 构造函数

构造函数描述
list()默认构造函数,创建空列表
list(size_type n)创建包含 n 个默认构造元素的列表
list(size_type n, const T& value)创建包含 n 个 value 的列表
list(const list& other)拷贝构造函数
list(list&& other)移动构造函数 (C++11)
list(InputIt first, InputIt last)用迭代器范围构造
list(std::initializer_list<T> init)初始化列表构造 (C++11)

3. 常用成员函数

3.1 元素访问

函数描述时间复杂度
front()访问第一个元素O(1)
back()访问最后一个元素O(1)

3.2 容量查询

函数描述时间复杂度
empty()检查是否为空O(1)
size()返回元素数量O(1) (C++11前可能为 O(n))
max_size()返回最大可能元素数量O(1)

3.3 修改器

函数描述时间复杂度
push_front(const T& value)在头部插入元素O(1)
push_front(T&& value)移动语义插入头部 (C++11)O(1)
pop_front()删除头部元素O(1)
push_back(const T& value)在尾部插入元素O(1)
push_back(T&& value)移动语义插入尾部 (C++11)O(1)
pop_back()删除尾部元素O(1)
insert(iterator pos, const T& value)在指定位置插入元素O(1)
emplace(iterator pos, Args&&... args)在指定位置构造元素 (C++11)O(1)
erase(iterator pos)删除指定位置元素O(1)
erase(iterator first, iterator last)删除范围元素O(n)
clear()清空所有元素O(n)
swap(list& other)交换两个列表内容O(1)

3.4 操作

函数描述时间复杂度
merge(list& other)合并两个已排序列表O(n)
sort()排序列表元素O(n log n)
reverse()反转列表顺序O(n)
unique()删除连续重复元素O(n)
remove(const T& value)删除所有等于 value 的元素O(n)
remove_if(Predicate pred)删除满足条件的元素O(n)
splice(iterator pos, list& other)将 other 的所有元素移动到当前列表的 pos 位置O(1)

4. 迭代器

std::list 提供双向迭代器:

迭代器描述
begin(), cbegin()返回指向第一个元素的迭代器
end(), cend()返回指向末尾(最后一个元素之后)的迭代器
rbegin(), crbegin()返回指向最后一个元素的反向迭代器
rend(), crend()返回指向开头(第一个元素之前)的反向迭代器

5. 示例代码

5.1 基本使用

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <iostream>
2 #include <list>
3
4 int main() {
5 // 创建并初始化列表
6 std::list<int> l = {1, 2, 3, 4, 5};
7
8 // 添加元素
9 l.push_back(6);
10 l.push_front(0);
11
12 // 遍历列表
13 for (int n : l) {
14 std::cout << n << " ";
15 }
16 std::cout << "\n"; // 输出: 0 1 2 3 4 5 6
17
18 // 插入元素
19 auto it = l.begin();
20 std::advance(it, 2); // 移动到第三个元素
21 l.insert(it, 99);
22
23 // 删除元素
24 l.remove(3); // 删除所有值为3的元素
25
26 // 排序
27 l.sort();
28
29 // 反转
30 l.reverse();
31
32 return 0;
33 }

5.2 合并和拼接示例

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <iostream>
2 #include <list>
3
4 int main() {
5 std::list<int> list1 = {1, 3, 5};
6 std::list<int> list2 = {2, 4, 6};
7
8 // 合并两个已排序列表
9 list1.merge(list2);
10 // list1: {1, 2, 3, 4, 5, 6}, list2: 空
11
12 std::list<int> list3 = {7, 8, 9};
13
14 // 拼接
15 auto it = list1.begin();
16 std::advance(it, 3); // 移动到第四个元素
17 list1.splice(it, list3);
18 // list1: {1, 2, 3, 7, 8, 9, 4, 5, 6}, list3: 空
19
20 return 0;
21 }

6. 性能考虑

6.1 优点

  • 在任何位置插入/删除元素都非常高效 (O(1))
  • 插入和删除不会使迭代器失效(除了被删除元素的迭代器)
  • 不需要连续内存,适合大元素或频繁插入删除的场景

6.2 缺点

  • 不支持随机访问,访问特定位置元素需要 O(n) 时间
  • 每个元素需要额外存储前后指针,内存开销较大
  • 缓存不友好,因为元素在内存中不连续

7. 与其它容器的比较

特性std::liststd::vectorstd::deque
存储结构双向链表动态数组分段数组
随机访问慢 (O(n))快 (O(1))快 (O(1))
头部插入/删除快 (O(1))慢 (O(n))快 (O(1))
尾部插入/删除快 (O(1))快 (O(1))快 (O(1))
中间插入/删除快 (O(1))慢 (O(n))慢 (O(n))
内存使用高 (每个元素额外指针)中等
迭代器失效仅删除元素影响插入/删除可能使所有迭代器失效中间插入/删除可能使所有迭代器失效

8. 最佳实践

  1. 适用场景

    • 需要频繁在任意位置插入/删除元素
    • 不需要随机访问元素
    • 元素较大,移动成本高
    • 需要保证迭代器长期有效(除了被删除元素)
  2. 不适用场景

    • 需要频繁随机访问元素
    • 内存受限的环境
    • 对缓存性能要求高的场景
  3. 性能提示

    • 对于小型元素,std::vector 通常性能更好
    • 使用 emplace 系列函数避免不必要的拷贝
    • 考虑使用 std::advancestd::next 移动迭代器

std::list 常用 API 的整理表格

名称用途原型代码示例注释
push_back在链表尾部插入元素void push_back(const T& value);std::list<int> l; l.push_back(42);添加元素到末尾,时间复杂度 O(1)。
push_front在链表头部插入元素void push_front(const T& value);l.push_front(10);添加元素到头部,时间复杂度 O(1)。
pop_back删除链表尾部元素void pop_back();l.pop_back();删除最后一个元素,链表不能为空。
pop_front删除链表头部元素void pop_front();l.pop_front();删除第一个元素,链表不能为空。
insert在指定位置插入元素iterator insert(iterator pos, const T& value);auto it = l.begin(); l.insert(it, 99);在迭代器 pos 前插入元素,返回新元素迭代器。
erase删除指定位置的元素iterator erase(iterator pos);it = l.begin(); it = l.erase(it);删除迭代器 pos 指向的元素,返回下一个元素的迭代器。
size返回链表元素数量size_type size() const;std::cout << l.size();时间复杂度 O(n)(C++11 前),可能 O(1)(实现依赖)。
empty检查链表是否为空bool empty() const;if (l.empty()) { /*...*/ }等价于 size() == 0,但可能更高效。
clear清空链表void clear();l.clear();删除所有元素,时间复杂度 O(n)。
front访问第一个元素T& front();int x = l.front();返回引用,链表不能为空。
back访问最后一个元素T& back();int y = l.back();返回引用,链表不能为空。
splice移动元素到另一个链表void splice(iterator pos, list& other);std::list<int> l2; l2.splice(l2.begin(), l);other 的所有元素移动到 pos 前,other 变为空。
remove删除所有等于指定值的元素void remove(const T& value);l.remove(42);遍历整个链表,删除所有匹配项。
sort对链表排序void sort();l.sort();默认升序排序,时间复杂度 O(n log n)。
merge合并两个已排序链表void merge(list& other);std::list<int> l2{1,3}; l.merge(l2);other 必须已排序,合并后 other 为空。
unique删除连续重复元素void unique();l.unique();通常需先排序,保留第一个出现的元素。
reverse反转链表元素顺序void reverse();l.reverse();时间复杂度 O(n)。

补充说明:

  1. 时间复杂度std::list 的插入/删除操作通常为 O(1)(已知迭代器位置时)。
  2. 迭代器失效:只有被删除元素的迭代器会失效,其他迭代器不受影响。
  3. 自定义比较sortmerge 可传入比较函数(如 l.sort(std::greater<int>()))。
文章目录