时间:2026-01-03 23:02
人气:
作者:admin
本文首发于我的个人博客:Better Mistakes
版权声明:本文为原创文章,转载请附上原文出处链接及本声明。
由于技术迭代较快,文章内容可能随时更新(含勘误及补充)。为了确保您看到的是最新版本,并获得更好的代码阅读体验,请访问:
在现代 C++ 开发中,虽然 std::vector 足以应付绝大多数的场景,但是在某些特定场景下,std::list 依旧是不可替代的神器。
#include <list>prev 和 next)连接l[5] 访问元素,必须从头一个一个遍历过去用法与 std::vector 非常相似:
#include <list>
std::list<int> l1; // 空链表
std::list<int> l2 = {1, 2, 3}; // 列表初始化
std::list<int> l3(l2); // 拷贝构造
std::list<int> l4(5, 100); // 5 个 元素,全是 100
std::vector 做不到的)这是 std::list 存在的理由。由于它是链表,它支持一些操作极其高效,或者提供了 std::vector 根本没有的接口。
std::vector 在头部插入 / 删除操作极其低效(\(O(N)\)),而 std::list 则是瞬间完成(\(O(1)\))。
push_front(val):头部插入pop_front():头部删除可以将一个 std::list 的元素(全部或部分)直接 “剪切” 并 “粘贴” 到另一个 std::list 中,这是纯指针操作,没有数据拷贝,所以速度极快。
std::list<int> list1 = {1, 2, 3};
std::list<int> list2 = {4, 5, 6};
auto it = list1.begin();
++it; // 指向 2
// 将 list2 的所有元素 “移动” 到 list1 的 2 前面
// list2 变空,list1 变为 {1, 4, 5, 6, 2, 3}
list1.splice(it, list2);
由于 std::list 无法随机访问,标准库算法(如 std::sort)对它无效。因此 list 自带了一套经过优化的成员函数:
l.sort():排序(稳定排序,底层通常是归并排序)。注意:千万别读 std::list 用 std::sort(l.begin(), l.end()),编译会报错l.remove(val):删除所有等于 val 的元素l.remove_if(pred):删除满足条件的元素l.unique():删除相邻的重复元素(去重前通常要先 sort)l.reverse():逆置链表l.merge(l2):合并两个已排序的链表(l2 会变空)这是决定选择 std::vector 还是 std::list 的关键因素。
++it、--it、*itit + 5,it < other_it(不能跳跃,不能比较大小)std::vector:std::vector 一旦扩容,所有迭代器全部失效;中间插入,后面所有迭代器失效。std::list 和 std::vector 的选择这是一个经典的 Trade-off(权衡)问题:
| 特性 | std::vector |
std::list |
|---|---|---|
| 随机访问 | \(O(1)\)(支持 []) |
不支持(\(O(N)\)) |
| 尾部插入 | \(O(1)\) | \(O(1)\) |
| 头部/中间插入 | \(O(N)\)(很慢,要挪动数据) | \(O(1)\)(很快,前提是有迭代器) |
| 内存布局 | 连续(Cache 命中率高) | 分散(Cache 命中率低) |
| 额外内存 | 较少(少量预留空间) | 较大(每个元素都要存两个指针) |
| 迭代器失效 | 容易失效 | 几乎不失效 |
结论:
std::vector:现代 CPU 的缓存机制非常依赖内存连续性。即使是在中间插入,对于小对象(如 int),std::vector 往往也比 std::list 快,因为 std::list 的遍历会导致大量的 Cache Miss。std::list:
std::forword_listC++11 引入了 std::forword_list(单向链表):
next 指针,没有 prev 指针std::list 更省内存(少存一个指针)???? 写在最后
如果你觉得这篇文章对你有帮助,欢迎到我的个人博客 Better Mistakes 逛逛。
在那里我归档了更多高质量的技术文章,也欢迎通过 RSS 订阅我的最新动态!
上一篇:override
下一篇:C++ 中的构造函数