题目
编写程序,演示 C++11 引入的单向链表容器 std::forward_list。验证其为达到与原生手写 C 单链表等同的极致轻量内存开销(每个节点仅多消耗一个 next 指针),因此不提供 size() 方法(保持 O(1) 空间)且不支持后向遍历;重点掌握其独有的 insert_after 和 erase_after 单链表指针拓扑操作。
输入格式:无需外部输入。
输出格式:输出 forward_list 单向链表构造、就地插入与遍历结果。
数据范围与约定:极简内存开销单向链表与 STL 零开销原则。
思路与算法
1. 为什么没有 size() 和 push_back()
若维护 size 会增加额外 8 字节结构体空间,若支持 push_back 需维护尾指针或退化为 O(N) 遍历。为践行“你不用就不为此付出代价”(Zero-overhead Principle),它只提供单向前进操作。
2. insert_after 的前驱机制
单链表插入节点必须修改待插入位置的前驱节点的 next 指针。因此标准库设计了 before_begin() 假头迭代器,用于支持在链表首个元素前插入。
3. 内存对比std::list 每个节点存两个指针(prev 与 next),在 64 位下指针开销 16 字节;std::forward_list 仅 1 个指针(8 字节),减半指针负担。
完整程序
#include <iostream>
#include <forward_list>
int main() {
std::forward_list<int> flist = {10, 20, 30};
// 1. 利用 before_begin() 在头部之前插入新首节点
flist.insert_after(flist.before_begin(), 5); // 变成 5, 10, 20, 30
// 2. 查找值为 10 的节点并在其后方插入 15
for (auto it = flist.begin(); it != flist.end(); ++it) {
if (*it == 10) {
flist.insert_after(it, 15);
break;
}
}
std::cout << "--- 1. insert_after 拓扑插入后的链表序列 ---\n";
for (int x : flist) {
std::cout << x << " -> ";
}
std::cout << "nullptr\n\n";
// 3. 删除 15 之后的节点(删除 20)
auto target = flist.begin();
while (target != flist.end() && *target != 15) {
++target;
}
if (target != flist.end()) {
flist.erase_after(target); // 删去 15 后面的 20
}
std::cout << "--- 2. erase_after 删除 20 后的链表序列 ---\n";
for (int x : flist) {
std::cout << x << " -> ";
}
std::cout << "nullptr\n";
return 0;
}运行示例
输出:
--- 1. insert_after 拓扑插入后的链表序列 ---
5 -> 10 -> 15 -> 20 -> 30 -> nullptr
--- 2. erase_after 删除 20 后的链表序列 ---
5 -> 10 -> 15 -> 30 -> nullptr