跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C算法

数据结构:C 语言单链表实现与原理

单链表是一种物理存储非连续的线性表结构,通过指针连接逻辑顺序。详细讲解了 C 语言下单链表的模拟实现,包括尾插、尾删、头插、头删、查找、插入及删除元素等操作接口。同时对比了链表与顺序表在存储结构、基本操作(创建、销毁、增删、查找)上的差异,指出链表适合频繁增删场景,而顺序表适合查询较多场景。代码示例涵盖了节点定义、内存管理及核心算法如链表逆置。

虚拟内存发布于 2026/3/16更新于 2026/10/16107 浏览

单链表

1. 链表的概念以及结构

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

单链表结构:

单链表结构示意图

注意:

  1. 从图上可以看出,单链表在逻辑结构上是连续的,但是物理结构上不一定连续。
  2. 在实际过程中我们单链表的节点都是从堆上申请出来的。
  3. 从堆上的空间是编译器分配出来的,因此申请的空间地址有可能连续,也有可能不连续。

2. 单链表的模拟实现

接口总览
typedef int SLDataType;
struct SListNode {
    SLDataType data;
    struct SListNode* next;
};

// 打印链表
void SLPrint(SLNode* plist);
// 尾插
void SLPushBack(SLNode** plist, SLDataType num);
// 头插
void SLPushFront(SLNode** plist, SLDataType num);
// 尾删
void SLPopBack(SLNode** plist);
// 头删
void SLPopFront(SLNode** plist);
// 查找链表中的元素
SLNode* SLFind(SLNode* plist, SLDataType num);
// 在任意位置插入元素
void SLInsert(SLNode** plist, SLNode* pos, SLDataType num);
// 删除任意位置的元素
 ;

 ;
void
SLErase
(SLNode** plist, SLNode* pos)
// 摧毁链表
void
SLDestroy
(SLNode** plist)
2.1 单链表的尾插和尾删
2.1.1 尾插
void SLPushBack(SLNode** pphead, SLDataType x) {
    assert(pphead);
    SLNode* newnode = CreateNode(x);
    // 判断空链表
    if (*pphead == NULL) {
        *pphead = newnode;
    } else {
        // 找尾
        SLNode* tail = *pphead;
        while (tail->next != NULL) {
            tail = tail->next;
        }
        tail->next = newnode;
    }
}
2.1.2 尾删
void SLPopBack(SLNode** pphead) {
    assert(pphead);
    assert(*pphead);
    // 1、一个节点
    // 2、一个以上节点
    if ((*pphead)->next == NULL) {
        free(*pphead);
        *pphead = NULL;
    } else {
        SLNode* tail = *pphead;
        while (tail->next->next != NULL) // 找到尾结点的前驱结点
        {
            tail = tail->next;
        }
        free(tail->next);
        tail->next = NULL;
    }
}
2.2 头插和头删
2.2.1 头插
void SLPushFront(SLNode** pphead, SLDataType x) {
    assert(pphead);
    SLNode* newnode = CreateNode(x);
    newnode->next = *pphead;
    *pphead = newnode;
}
2.2.2 头插的应用(链表的逆置)

反转链表 根据头插法将源链表的头结点,头插入新建的 newNode 链表中。

逆置步骤 1

逆置步骤 2

逆置步骤 3

逆置步骤 4

struct ListNode* ReverseList(struct ListNode* head) {
    struct ListNode* cur = head;
    struct ListNode* newNode = NULL;
    while (cur != NULL) {
        struct ListNode* next = cur->next;
        cur->next = newNode;
        newNode = cur;
        cur = next;
    }
    return newNode;
}
2.2.3 头删
void SLPopFront(SLNode** pphead) {
    assert(pphead);
    assert(*pphead);
    SLNode* tmp = *pphead;
    *pphead = (*pphead)->next;
    free(tmp);
}
2.3 查找链表中的元素

按值查找 时间复杂度为:O(N)

SLNode* SLFind(SLNode* phead, SLDataType x) {
    SLNode* cur = phead;
    while (cur) {
        if (cur->data == x) {
            return cur;
        } else {
            cur = cur->next;
        }
    }
    return NULL;
}
2.4 在任意位置插入元素
void SLInsert(SLNode** pphead, SLNode* pos, SLDataType x) {
    // 严格限定 pos 一定是链表里面的一个有效节点
    assert(pphead);
    assert(pos);
    assert(*pphead);
    if (*pphead == pos) {
        // 头插
        SLPushFront(pphead, x);
    } else {
        SLNode* prev = *pphead;
        while (prev->next != pos) {
            prev = prev->next;
        }
        SLNode* newnode = CreateNode(x);
        prev->next = newnode;
        newnode->next = pos;
    }
}
2.5 删除任意位置的元素
void SLErase(SLNode** pphead, SLNode* pos) {
    assert(pphead);
    assert(*pphead);
    assert(pos);
    if (*pphead == pos) {
        // 头删
        SLPopFront(pphead);
    } else {
        SLNode* prev = *pphead;
        while (prev->next != pos) {
            prev = prev->next;
        }
        prev->next = pos->next;
        free(pos);
        pos = NULL;
    }
}
2.6 遍历链表 和 销毁链表

时间复杂度为 O(N)

void SLPrint(SLNode* phead) {
    SLNode* cur = phead;
    while (cur != NULL) {
        printf("%d->", cur->data);
        cur = cur->next;
    }
    printf("NULL\n");
}
void SLDestroy(SLNode** pphead) {
    assert(pphead);
    SLNode* cur = *pphead;
    while (cur) {
        SLNode* next = cur->next;
        free(cur);
        cur = next;
    }
    *pphead = NULL;
}

3. 链表 VS 顺序表

3.1 比较存储结构

顺序表

  1. 优点:支持随机存储,存储密度高
  2. 缺点:大片连续空间分配不方便,更改容量不方便

链表

  1. 优点:离散的小空间分配方便,方便更改容量
  2. 缺点:不可以随机存取,存储密度低
3.2 从基本操作比较

一、创建 顺序表

  1. 需要分配大片连续的空间,若空间过小则之后不方便扩容,若空间过大则回造成空间的浪费
  2. 静态分配:数组容量不可改变
  3. 动态分配:容量可以改变单是存入数据需要移动大量的元素,时间代价高

链表

  1. 只需要分配结点即可,之后方便拓展
  2. 相较于顺序表灵活性高

二、销毁 顺序表

  1. 静态:系统回收
  2. 动态:需要手动 free 回收堆中的空间

链表

  1. 一次删除各个结点 free,注意要将链表置空

三、增加和删除 顺序表:增删都需要移动元素,前移或是后移 时间复杂度为 O(N) 开销主要来自移动元素(代价高) 链表:只需要插入和删除修改指针既可 时间复杂度为 O(N),开销主要来自查找目标元素(代价底)

四、查找 顺序表:按位查找时间复杂度为 O(1)、按值查找时间复杂度为 O(N) 有序情况下为 O(logn) 链表:按位查找时间复杂度为 O(N)、按值查找时间复杂度为 O(N) 都只能从头结点开始查找

注:顺序表的查找效率比链表高

链表——表长难以估计、经常要增加和删除元素。 顺序表——表长可以估计、查询操作较多。

目录

  1. 单链表
  2. 1. 链表的概念以及结构
  3. 2. 单链表的模拟实现
  4. 接口总览
  5. 2.1 单链表的尾插和尾删
  6. 2.1.1 尾插
  7. 2.1.2 尾删
  8. 2.2 头插和头删
  9. 2.2.1 头插
  10. 2.2.2 头插的应用(链表的逆置)
  11. 2.2.3 头删
  12. 2.3 查找链表中的元素
  13. 2.4 在任意位置插入元素
  14. 2.5 删除任意位置的元素
  15. 2.6 遍历链表 和 销毁链表
  16. 3. 链表 VS 顺序表
  17. 3.1 比较存储结构
  18. 3.2 从基本操作比较

更多推荐文章

查看全部
  • 在 JetBrains IDE 中配置和使用 Claude Code 工具
  • AI 大模型赋能医疗行业的应用场景与部署挑战
  • Python 基础语法与核心概念详解
  • C++ 智能指针详解:unique_ptr、shared_ptr 与 weak_ptr
  • Git 基础与进阶使用指南
  • Cursor 中 MCP 服务配置与使用指南
  • AI 小说生成器本地部署教程
  • SQL 核心概念:JOIN 和 UNION 的区别
  • Draw.io 官方 MCP Server 实测:AI 自动生成架构图
  • 3 月 1 日 AI 前沿动态:Agent 进化与模型新突破
  • OpenClaw 集成 GitHub Copilot 配置指南
  • Docker 在 Ubuntu 系统上的详细安装指南
  • 网络安全攻防领域核心证书解析:CISP-PTE/PTS/IRE/IRS 详解
  • React Native 鸿蒙化集成 react-native-webview 实战
  • 算电协同技术原理与架构
  • ComfyUI-Manager 插件管理工具与 AI 绘画环境搭建
  • 使用 Optuna 在 PyTorch 中进行超参数优化
  • Java 调用通义千问与 DeepSeek 大模型实战指南
  • LLaMA-2 与 Mixtral 的提示词调优技巧
  • Git 多人协作开发流程与冲突解决

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online