插入节点前必须检查头指针是否为空,空链表头插可与非空链表统一处理为newNode->next = head; head = newNode;,但尾插必须区分空链表(等价头插)与非空链表(需遍历至末尾)。

插入节点前必须先确认头指针是否为空
很多初学者在写 insert 时直接对 head->next 操作,结果一运行就 Segmentation fault。根本原因是没判断 head 是否为 nullptr,尤其在空链表中插入首个节点时,head 本身就是空的,不能解引用。
实操建议:
- 所有插入函数入口第一行加
if (head == nullptr) { head = newNode; return; } - 若采用带头结点(dummy node)方式,则头指针永远非空,但需注意:插入逻辑要从
dummy->next开始,且最终返回的是dummy->next,不是dummy - 插入位置为第 0 位(即头插)时,无论链表是否为空,都应统一用
newNode->next = head; head = newNode;,比条件分支更简洁安全
头插、尾插、中间插的指针操作差异
三种插入方式看似只是位置不同,但指针修改顺序和依赖关系完全不同。错一步,就会丢节点或成环。
常见错误现象:插入后原链表“消失”、新节点没连上、next 指向随机地址(未初始化)。
立即学习“C++免费学习笔记(深入)”;
实操建议:
-
头插:先设
newNode->next = head;,再更新head = newNode;—— 顺序不能反,否则会丢失原链表 -
尾插:必须遍历到末尾(
curr != nullptr && curr->next != nullptr),然后curr->next = newNode;;别忘了newNode->next = nullptr;,否则可能指向脏内存 -
中间插(pos 索引):需找到第
pos-1个节点(如插在索引 2 处,要停在索引 1 节点),再执行newNode->next = prev->next; prev->next = newNode;
new 分配节点后必须检查是否分配成功
C++ 中 new 在内存不足时默认抛出 std::bad_alloc,但若用了 new(std::nothrow),则返回 nullptr。不检查就直接用,等于埋雷。
实操建议:
- 基础写法:
Node* newNode = new Node(val); if (newNode == nullptr) { throw std::runtime_error("Failed to allocate node"); } - 更健壮的做法是封装为工厂函数:
Node* makeNode(int val) { auto p = new Node(val); if (!p) throw std::bad_alloc{}; return p; } - 避免在循环插入中反复
new后不处理异常,导致部分节点插入、部分失败,链表处于中间断裂状态
插入后忘记更新 size 或引发迭代器失效
如果链表类里维护了 size_ 成员,每次插入都必须 ++size_;否则 size() 返回值永远不准。另外,C++ 标准库没有链表迭代器失效规则可套用——自实现链表中,插入操作本身不使其他节点指针失效,但若用户缓存了某节点指针并期望它“始终有效”,要注意:该指针仍有效,但其逻辑位置可能已变(比如头插后,原来第一个节点变成第二个)。
实操建议:
- 所有插入路径(头/尾/中间/空表)都要覆盖
++size_,包括构造首个节点时 - 若提供
begin()/end()迭代器接口,插入不改变已有节点地址,所以不触发迭代器失效;但不要假设“插入后某个迭代器仍指向‘当前第 n 个’元素”,因为链表结构已变 - 调试时可在插入前后打印
size_和遍历输出,快速验证是否漏更新或逻辑错位
实际写插入函数时,最容易被忽略的是:空链表头插与非空链表头插,代码可以完全一致,不需要 if 分支;但尾插必须区分空链表(此时等价于头插)和非空链表(需遍历),这个分支躲不开,也别硬凑成一种写法。











