以下是 C++ 中 std::list
的详细使用指南,涵盖其特性、常用操作、性能分析及典型应用场景:
1. 基本概念
定义:
std::list
是双向链表,支持双向遍历,元素在内存中非连续存储。头文件:
#include <list>
核心特性:
任意位置插入/删除高效(O(1) 时间复杂度)。
不支持随机访问(需通过迭代器顺序访问)。
每个节点存储前驱和后继指针,内存开销略高(每个元素多 2 个指针)。
2. 初始化与构造
(1) 空链表
std::list<int> lst; // 空链表
(2) 指定大小和初始值
std::list<int> lst1(5); // 5个元素,默认初始化为0 std::list<int> lst2(5, 42); // 5个元素,初始化为42
(3) 列表初始化(C++11+)
std::list<int> lst = {1, 2, 3, 4, 5}; std::list<std::string> names {"Alice", "Bob"};
(4) 从其他容器构造
std::vector<int> vec {10, 20, 30}; std::list<int> lst3(vec.begin(), vec.end()); // 复制vec的元素 std::list<int> lst4(lst3); // 拷贝构造
3. 元素访问
(1) 迭代器遍历
// 正向遍历 for (auto it = lst.begin(); it != lst.end(); ++it) { std::cout << *it << " "; } // 反向遍历 for (auto rit = lst.rbegin(); rit != lst.rend(); ++rit) { std::cout << *rit << " "; } // 范围for循环(C++11+) for (const auto& num : lst) { std::cout << num << " "; }
(2) 首尾元素访问
int first = lst.front(); // 首元素 int last = lst.back(); // 尾元素
4. 添加/删除元素
(1) 头部和尾部操作
lst.push_front(0); // 头部插入元素 lst.emplace_front(-1); // 更高效(C++11+,避免拷贝) lst.push_back(6); // 尾部插入元素 lst.emplace_back(7); // 直接构造元素 lst.pop_front(); // 删除头部元素 lst.pop_back(); // 删除尾部元素
(2) 任意位置插入/删除
auto it = lst.begin(); std::advance(it, 2); // 移动迭代器到第3个位置 lst.insert(it, 99); // 在it前插入99 it = lst.erase(it); // 删除it指向的元素,返回下一个元素的迭代器
(3) 批量操作
lst.remove(42); // 删除所有值为42的元素 lst.remove_if([](int x) { // 删除满足条件的元素(Lambda表达式) return x % 2 == 0; });
5. 链表特有操作
(1) 合并链表(merge
)
std::list<int> lstA {1, 3, 5}; std::list<int> lstB {2, 4, 6}; lstA.merge(lstB); // 合并后lstA有序,lstB变为空
(2) 拼接链表(splice
)
// 将lstB的全部元素移动到lstA的指定位置 auto pos = lstA.begin(); lstA.splice(pos, lstB); // lstB变为空 // 移动lstB的单个元素到lstA auto it = lstB.begin(); lstA.splice(pos, lstB, it); // 移动lstB的某个区间到lstA auto start = lstB.begin(); auto end = lstB.end(); lstA.splice(pos, lstB, start, end);
(3) 排序与去重
lst.sort(); // 链表内部排序(O(n log n)) lst.unique(); // 删除连续重复元素 lst.unique([](int a, int b) { // 自定义去重条件 return std::abs(a - b) <= 1; });
6. 容量管理
int size = lst.size(); // 元素数量 bool isEmpty = lst.empty(); // 是否为空 lst.resize(10); // 调整大小,多出元素默认初始化 lst.resize(15, 42); // 调整到15个元素,新增元素初始化为42
7. 性能优化与注意事项
(1) 适用场景
需要频繁在任意位置插入/删除元素(如实现 LRU 缓存)。
不需要随机访问,只需顺序或双向遍历。
需避免迭代器失效(插入/删除操作不会使其他迭代器失效)。
(2) 避免低效操作
随机访问:
list
不支持operator[]
,需通过迭代器逐步移动。auto it = lst.begin(); std::advance(it, n); // O(n) 时间复杂度
(3) 迭代器失效规则
删除元素时,指向被删元素的迭代器失效,其他迭代器不受影响。
插入元素时,所有迭代器保持有效。
8. 示例代码
#include <list> #include <iostream> #include <algorithm> int main() { std::list<int> nums = {3, 1, 4, 1, 5}; nums.push_front(0); // 头部插入0 nums.emplace_back(6); // 尾部直接构造6 // 删除所有值为1的元素 nums.remove(1); // 自定义排序(降序) nums.sort([](int a, int b) { return a > b; }); // 合并另一个链表 std::list<int> other {2, 7}; nums.merge(other); // 合并后nums为降序,other为空 // 输出结果 for (int num : nums) { std::cout << num << " "; // 输出:7 6 5 4 3 2 0 } return 0; }
9. 与 std::vector
对比
特性 | std::list | std::vector |
---|---|---|
内存布局 | 非连续(链表节点) | 连续(动态数组) |
随机访问 | 不支持(O(n)) | 支持(O(1)) |
插入/删除(中间) | O(1) | O(n) |
迭代器失效 | 仅被删元素失效 | 可能全部失效(扩容时) |
缓存友好性 | 差(内存碎片化) | 好(连续内存) |
10. 典型应用场景
高频插入/删除:如实现队列(
std::deque
更优)、LRU 缓存。稳定迭代器:在遍历过程中频繁修改链表结构(如游戏中的实体管理)。
复杂操作:需要合并、反转、去重等链表特有操作。
通过掌握 std::list
的特性和操作,可以在特定场景下显著提升代码效率。但需注意:在大多数需要动态数组的场景中,优先选择 std::vector
,仅在需要频繁中间操作时使用 std::list
。
系统当前共有 438 篇文章