c++中如何实现单链表的节点插入_c++链表操作基础教程【实例】

插入节点前必须检查头指针是否为空,空链表头插可与非空链表统一处理为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 指向随机地址(未初始化)。

实操建议:

  • 头插:先设 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 分支;但尾插必须区分空链表(此时等价于头插)和非空链表(需遍历),这个分支躲不开,也别硬凑成一种写法