必须包含头文件,声明空链表为std::list lst;支持初值列表初始化和范围构造,但不支持size+value构造;仅能用迭代器增删查改,不可用下标访问。

如何正确初始化和声明 std::list
直接用 std::list 前必须包含头文件 ,它不支持随机访问,也不提供 operator[]。常见错误是当成 std::vector 用,比如写 my_list[0] —— 这会编译失败。
- 声明空链表:
std::listlst; - 带初值列表初始化(C++11 起):
std::list<:string> names = {"Alice", "Bob", "Charlie"}; - 用另一个容器范围构造:
std::listlst(other_vec.begin(), other_vec.end()); - 注意:不能用
std::list这种“大小+默认值”方式初始化(那是lst(10, 42); vector的语法)
插入和删除操作必须用迭代器,不能用下标
std::list 是双向链表,所有增删都在常数时间完成,但前提是用对接口。误用 push_back() 或 erase() 的迭代器版本以外的方式,容易导致逻辑错乱或性能退化。
- 头部插入:
lst.push_front(10); - 尾部插入:
lst.push_back("hello"); - 任意位置插入(在
it之前):lst.insert(it, 99); - 删除单个元素:
lst.erase(it);(it必须合法且未失效) - 删除满足条件的所有元素:
lst.remove_if([](int x) { return x % 2 == 0; }); - ⚠️ 错误示范:
lst.erase(2);——erase()没有接受整数索引的重载,这行不通
遍历只能用迭代器或范围 for,不可用下标访问
因为没有 operator[] 和 at(),任何试图“取第 n 个元素”的操作都得靠移动迭代器,时间复杂度是 O(n)。别为了图方便写循环 + std::next(it, n) 来模拟下标——那说明你可能该换容器了。
- 正向遍历推荐写法:
for (auto it = lst.begin(); it != lst.end(); ++it) { std::cout << *it << "\n"; } - 更简洁的范围 for:
for (const auto& x : lst) { std::cout << x << "\n"; } - 反向遍历:
for (auto rit = lst.rbegin(); rit != lst.rend(); ++rit) { std::cout << *rit << "\n"; } - 想取首/尾元素?用
lst.front()/lst.back(),但务必确保非空,否则未定义行为
list 的 splice、merge、sort 是独有优势,别忽略
这些成员函数不复制元素,只调整指针,是 list 相比其他序列容器的核心价值。尤其 splice() 可以零成本转移节点,常被用于实现任务队列、LRU 缓存等场景。
立即学习“C++免费学习笔记(深入)”;
- 把另一个 list 的全部内容接过来:
lst1.splice(lst1.end(), lst2);(执行后lst2为空) - 只接某一段:
lst1.splice(lst1.begin(), lst2, it1, it2);(左闭右开区间) - 合并两个已排序 list:
lst1.merge(lst2);(lst2会被清空) - 原地排序:
lst.sort();(支持自定义比较:lst.sort(std::greater)()); - ⚠️ 注意:
sort()和merge()要求元素可比较;splice()不调用拷贝/移动构造,所以对不可拷贝类型(如std::unique_ptr)也安全
std::list,关键是放弃“按位置操作”的思维惯性。它的优势不在索引,而在稳定迭代器、高效局部增删、以及零拷贝的节点重组能力。一旦开始频繁调用 std::advance 或手写循环找第 N 个元素,就该重新评估是否选错了容器。










