数据结构:在链表中插入节点(Inserting in a Linked List)

目录

问题本质:什么是"插入一个节点"?

链表插入分为三种情况

情况一:插入在表头

情况二:插入在中间位置

情况三:插入在末尾

实现插入函数

[为什么插入函数要写成 struct Node** head?](#为什么插入函数要写成 struct Node** head?)


首先,为什么要在链表中插入?

  • 在数组中,插入元素需要移动后面的所有元素

  • 而链表中,节点之间是通过指针连接的,不需要整体移动

  • 只需要修改两个指针即可完成插入操作

问题本质:什么是"插入一个节点"?

我们要把一个新节点 插入到链表中的某个位置。本质上是在链表中"夹进去"一个节点。

这意味着:

改变原有两个节点之间的指针,使它们包住新的节点

假设你已经有一个链表:

复制代码
head → [10] → [20] → [30] → NULL

现在你要插入一个值为 15 的新节点,插入在 1020 之间。

插入后变成:

复制代码
head → [10] → [15] → [20] → [30] → NULL

✅ 所以插入的本质是:改变两个指针

cpp 复制代码
struct Node* prev; // 插入位置的前一个节点(如10)
struct Node* newNode; // 新节点

newNode->next = prev->next;
prev->next = newNode;

意思是:

  • 先让新节点指向原来的"下一个"

  • 再让"前一个"节点指向新节点


链表插入分为三种情况

我们按位置分类,分别推导指针操作:

情况一:插入在表头

假设你要把新节点插入到链表最前面。

原链表:

cpp 复制代码
head → [10] → [20] → ...

操作后:

cpp 复制代码
head → [newNode] → [10] → [20] → ...
cpp 复制代码
newNode->next = head;
head = newNode;
  • newNodenext 指向原来的头

  • head 指向新节点

情况二:插入在中间位置

假设你要把 15 插入在 1020 之间:

cpp 复制代码
head → [10] → [20] → [30]
              ↑
           插入在这里

你需要:

  • 一个指针 prev 指向 10

  • 一个新节点 newNode 值为 15

操作步骤:

cpp 复制代码
newNode->next = prev->next; // 指向 20
prev->next = newNode;       // 10 指向 15

情况三:插入在末尾

你想把新节点加在最后:

cpp 复制代码
head → [10] → [20] → [30] → NULL
                              ↑
                           插入在这里

需要:

  • 遍历到尾部,找到最后一个节点 tail,即 tail->next == NULL

  • 执行:

cpp 复制代码
tail->next = newNode;
newNode->next = NULL;

| 插入位置 | 操作顺序 |
| 头部插入 | newNode->next = head; head = newNode; |
| 中间插入 | 找到 prevnewNode->next = prev->next; prev->next = newNode; |

尾部插入 遍历到尾部,tail->next = newNode; newNode->next = NULL;

实现插入函数

第一步:函数设计

我们假设链表下标从 0 开始:

cpp 复制代码
insertAtPosition(struct Node** head, int pos, int value);
  • pos == 0:插入到头部

  • pos == 1:插入到第二个节点之前

  • ...

  • pos == n:插入到尾部

参数解释:

  • head: 指向链表头指针的指针(因为头可能会改变)

  • pos: 插入的位置(下标从 0 开始)

  • value: 插入的整数值


为什么插入函数要写成 struct Node** head?

链表的"头节点"是通过一个指针 struct Node* head 表示的。如果你把 head 传进函数,函数里能改变它吗?

在 C 语言中,所有函数参数都是按值传递的,包括指针。

也就是说:

cpp 复制代码
void f(struct Node* head) {
    head = someNewNode;
}

↑ 这个 head 是函数 f局部副本 ,你在里面给 head 赋新值,不会改变原来的 head

因为我们想让函数内部能修改传入的 head 指针本身。

也就是说:

  • *head 是真正的头指针变量

  • head 是指向那个头指针变量的地址

你要改变的内容 参数应该传什么
节点的内容(data) 节点指针(struct Node*
指针本身(head) 指针的地址(struct Node**

类比指针和变量:

cpp 复制代码
int x = 10;

void change(int a) { a = 20; }           // 无效
void change2(int* p) { *p = 20; }        // 有效

int main() {
    change(x);      // 不会改变 x
    change2(&x);    // 才能真的改变 x 的值
}

第二步:先创建一个新节点

在插入之前,你必须先创建一个节点,用来存储 value

cpp 复制代码
struct Node* newNode = (struct Node*) malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = NULL;

为什么 next = NULL

  • 初始状态下你还没连接它,为了避免野指针,最好先清空。

第三步:处理特殊情况 ------ 插入在头部

pos == 0,说明新节点要放在链表开头:

cpp 复制代码
newNode->next = *head;  // 新节点指向原来的头
*head = newNode;        // 新节点变成新的头
return;

第四步:插入在中间或尾部

我们需要:

  • 遍历链表,找到pos-1 个节点(也就是新节点的"前一个")

  • 然后按顺序修改指针

cpp 复制代码
struct Node* temp = *head;
for (int i = 0; i < pos - 1; i++) {
    if (temp == NULL) {
        printf("Position out of bounds.\n");
        free(newNode);
        return;
    }
    temp = temp->next;
}

第五步:指针修改连接链

此时 temp 是插入点的前驱节点:

cpp 复制代码
newNode->next = temp->next;
temp->next = newNode;

完整代码

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node* next;
};

void insertAtPosition(struct Node** head, int pos, int value) {
    struct Node* newNode = (struct Node*) malloc(sizeof(struct Node));
    newNode->data = value;
    newNode->next = NULL;

    if (pos == 0) {
        // 插入到头部
        newNode->next = *head;
        *head = newNode;
        return;
    }

    // 找到前一个节点
    struct Node* temp = *head;
    for (int i = 0; i < pos - 1 && temp != NULL; i++) {
        temp = temp->next;
    }

    if (temp == NULL) {
        printf("Position out of bounds.\n");
        free(newNode);
        return;
    }

    // 插入
    newNode->next = temp->next;
    temp->next = newNode;
}

void printList(struct Node* head) {
    while (head != NULL) {
        printf("%d → ", head->data);
        head = head->next;
    }
    printf("NULL\n");
}

int main() {
    struct Node* head = NULL;

    // 插入几个节点
    insertAtPosition(&head, 0, 30); // 30
    insertAtPosition(&head, 0, 20); // 20 30
    insertAtPosition(&head, 0, 10); // 10 20 30
    insertAtPosition(&head, 2, 15); // 10 20 15 30
    insertAtPosition(&head, 4, 40); // 10 20 15 30 40

    printList(head);
    return 0;
}
相关推荐
tobias.b2 小时前
计算机基础知识-数据结构
java·数据结构·考研
不想看见4045 小时前
Valid Parentheses栈和队列--力扣101算法题解笔记
开发语言·数据结构·c++
计算机安禾5 小时前
【C语言程序设计】第37篇:链表数据结构(一):单向链表的实现
c语言·开发语言·数据结构·c++·算法·链表·蓝桥杯
皮卡狮6 小时前
高阶数据结构:AVL树
数据结构·算法
不要秃头的小孩6 小时前
50. 随机数排序
数据结构·python·算法
故事和你917 小时前
sdut-python-实验四-python序列结构(21-27)
大数据·开发语言·数据结构·python·算法
丶小鱼丶8 小时前
数据结构和算法之【栈】
java·数据结构
不要秃头的小孩8 小时前
力扣刷题——111.二叉树的最小深度
数据结构·python·算法·leetcode
散峰而望8 小时前
【基础算法】从入门到实战:递归型枚举与回溯剪枝,暴力搜索的初级优化指南
数据结构·c++·后端·算法·机器学习·github·剪枝
elseif12310 小时前
CSP-S提高级大纲
开发语言·数据结构·c++·笔记·算法·大纲·考纲