单向链表插入需分头插、尾插、中间插三类处理:头插更新head;尾插遍历至curr->next==nullptr;中间插先验证i-1位置合法性,越界则返回;所有插入均需new分配并异常时delete防泄漏。

如何用 C++ 正确实现单向链表的节点插入(头插/尾插/中间插)
头插和尾插容易写对,但中间插入常因指针移动逻辑错乱导致 nullptr 解引用或跳过节点。关键不是“写个循环”,而是明确插入点前驱节点是否存在。
实操建议:
- 头插只需更新
head指针:新节点next指向原head,再令head = newNode - 尾插必须遍历到末尾(
curr->next == nullptr),不能只判断curr == nullptr—— 否则会把新节点挂到空指针上 - 中间插入(如在第 i 个位置)要先检查
i-1是否合法:若i == 0应走头插;若遍历完仍不到第i-1个节点,说明越界,直接返回 - 所有插入都需用
new分配节点,且插入失败时记得delete newNode防止内存泄漏
删除节点时为什么总出现 double free 或野指针?
根本原因是没区分“删第一个节点”和“删后续节点”两种路径,统一用 prev->next = curr->next 时,prev 在头节点场景下是 nullptr,解引用就崩了。
实操建议:
- 删除前先判空:
if (head == nullptr) return; - 若要删头节点(比如按值删且
head->val == target),直接Node* tmp = head; head = head->next; delete tmp; - 删非头节点必须维护
prev和curr两个指针,且prev初始为head,curr从head->next开始遍历 - 删完记得将被删节点的
next置为nullptr(可选但推荐),避免后续误用
就地逆序单向链表:三指针法为什么比递归更稳妥?
递归逆序在链表过长时易触发栈溢出(比如 10w 节点),而三指针迭代法空间复杂度稳定为 O(1),且无函数调用开销。
实操建议:
- 初始化三个指针:
prev = nullptr,curr = head,next = nullptr - 循环中先保存
curr->next到next,再让curr->next = prev,然后prev = curr、curr = next - 循环结束条件是
curr == nullptr,此时prev就是新头节点,直接返回prev - 别漏掉边界处理:空链表或单节点链表直接返回原
head即可,无需进循环
调试时怎么看链表是否真被修改了?
光靠 cout 打印 head 地址没用——地址不变不代表结构没变。真正有效的是打印整条链的值序列,同时观察节点间 next 指针是否形成闭环或断裂。
实操建议:
- 写一个简易打印函数:
void printList(Node* head),内部用while (curr != nullptr)遍历并输出curr->val - 在关键操作前后都调用它,比如插入后立刻 print,确认新值是否出现在预期位置
- 若发现输出卡住或重复,大概率是
next指针成环(比如逆序时少更新某个next);若提前终止,可能是某处next被设为nullptr过早 - 用
assert(curr != head)类似的断言辅助查环(仅调试期),避免发布版性能损耗
链表操作真正的复杂点不在算法本身,而在每一步指针赋值的时机和对象是否还有效。哪怕只改一行 next,也可能让后续所有遍历失效。写完务必用长度为 0、1、2、5 的链表各跑一遍,再看输出和 val 序列是否符合直觉。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











