深入链表剖析:从原理到 C 语言实现,涵盖单向、双向及循环链表全解析

news2025/12/14 13:06:35

1 引言

        在数据结构的学习中,链表是一种基础且极为重要的线性数据结构。与数组不同,链表通过指针将一系列节点连接起来,每个节点包含数据域和指向下一个节点的指针域。这种动态的存储方式使得链表在插入、删除等操作上具有独特的优势。本文将深入探讨链表的原理、分类、实现方法及其在实际应用中的表现,所有实现均使用 C 语言完成。


2 链表基础概念

2.1 链表定义

        链表由一系列节点组成,每个节点至少包含两部分信息:数据域(存储数据)和指针域(存储下一个节点的地址)。根据指针域的数量和连接方式,链表可以分为单向链表、双向链表和循环链表等。

2.2 链表特点

  • 动态性:链表的大小在运行时可以动态变化,无需预先分配固定空间。
  • 灵活性:插入和删除操作通常只需修改指针,无需移动大量数据。
  • 内存消耗:每个节点需要额外的指针域,因此相比数组,链表在内存使用上更为“浪费”。

3 链表分类与实现

3.1 单向链表

3.1.1 定义与结构

        单向链表是最简单的链表形式,每个节点包含一个数据域和一个指向下一个节点的指针。

#include <stdio.h>
#include <stdlib.h>

// 定义单向链表节点
typedef struct Node {
    int data;
    struct Node* next;
} Node;

3.1.2 基本操作实现

  • 创建节点
Node* createNode(int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    if (newNode == NULL) {
        printf("Memory allocation failed.\n");
        exit(1);
    }
    newNode->data = data;
    newNode->next = NULL;
    return newNode;
}
  • 在链表头部插入节点
void insertAtHead(Node** head, int data) {
    Node* newNode = createNode(data);
    newNode->next = *head;
    *head = newNode;
}
  • 在链表尾部插入节点
void insertAtTail(Node** head, int data) {
    Node* newNode = createNode(data);
    if (*head == NULL) {
        *head = newNode;
        return;
    }
    Node* temp = *head;
    while (temp->next != NULL) {
        temp = temp->next;
    }
    temp->next = newNode;
}
  • 删除节点
void deleteNode(Node** head, int key) {
    Node* temp = *head;
    Node* prev = NULL;

    if (temp != NULL && temp->data == key) {
        *head = temp->next;
        free(temp);
        return;
    }

    while (temp != NULL && temp->data != key) {
        prev = temp;
        temp = temp->next;
    }

    if (temp == NULL) {
        printf("Key not found.\n");
        return;
    }

    prev->next = temp->next;
    free(temp);
}

3.2 双向链表

3.2.1 定义与结构

        双向链表每个节点包含一个数据域和两个指针域,分别指向下一个节点和前一个节点。

typedef struct DoublyNode {
    int data;
    struct DoublyNode* prev;
    struct DoublyNode* next;
} DoublyNode;

3.2.2 基本操作实现

  • 创建双向链表节点
DoublyNode* createDoublyNode(int data) {
    DoublyNode* newNode = (DoublyNode*)malloc(sizeof(DoublyNode));
    if (newNode == NULL) {
        printf("Memory allocation failed.\n");
        exit(1);
    }
    newNode->data = data;
    newNode->prev = NULL;
    newNode->next = NULL;
    return newNode;
}
  • 在双向链表头部插入节点
void insertDoublyAtHead(DoublyNode** head, int data) {
    DoublyNode* newNode = createDoublyNode(data);
    if (*head == NULL) {
        *head = newNode;
        return;
    }
    newNode->next = *head;
    (*head)->prev = newNode;
    *head = newNode;
}

3.3 循环链表

3.3.1 定义与结构

        循环链表可以是单向或双向的,其特点是最后一个节点的指针域指向第一个节点,形成一个环。

3.3.2 基本操作实现(以单向循环链表为例)

  • 在循环链表尾部插入节点
void insertCircularAtTail(Node** head, int data) {
    Node* newNode = createNode(data);
    if (*head == NULL) {
        *head = newNode;
        newNode->next = *head; // 指向自己,形成环
        return;
    }
    Node* temp = *head;
    while (temp->next != *head) { // 遍历到最后一个节点
        temp = temp->next;
    }
    temp->next = newNode;
    newNode->next = *head; // 新节点的next指向头节点
}

4 链表的应用场景

        链表在许多场景中都有广泛的应用,包括但不限于:

  • 实现栈和队列:链表可以方便地实现栈和队列的后进先出(LIFO)和先进先出(FIFO)特性。
  • 动态内存分配:链表可以用于管理动态分配的内存块,如内存池。
  • 图形和树结构:在图的邻接表表示和树的非线性结构中,链表常用于存储子节点或相邻节点。
  • 哈希表冲突解决:在哈希表中,当发生冲突时,可以使用链表来存储冲突的键值对。

5 链表与数组的比较

特性链表数组
内存分配动态,运行时分配静态或动态,但通常预先分配固定大小
插入/删除高效,只需修改指针低效,需要移动大量数据
随机访问不支持,需从头遍历支持,通过索引直接访问
内存消耗每个节点需额外指针域,内存消耗较大紧凑存储,内存消耗较小

6 结论

        链表作为一种重要的数据结构,在动态数据存储和操作中发挥着不可替代的作用。通过本文的深入剖析,我们了解了链表的基本概念、分类、实现方法及其应用场景。链表与数组各有优劣,在实际应用中应根据具体需求选择合适的数据结构。未来,随着数据结构与算法研究的深入,链表及其变体将在更多复杂场景中展现其独特的价值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2393043.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[9-1] USART串口协议 江协科技学习笔记(13个知识点)

1 2 3 4全双工就是两个数据线&#xff0c;半双工就是一个数据线 5 6 7 8 9 10 TTL&#xff08;Transistor-Transistor Logic&#xff09;电平是一种数字电路中常用的电平标准&#xff0c;它使用晶体管来表示逻辑状态。TTL电平通常指的是5V逻辑电平&#xff0c;其中&#xff1a;…

Oracle基础知识(五)——ROWID ROWNUM

目录 一、ROWID 伪列 二、ROWNUM——限制查询结果集行数 1.ROWNUM使用介绍 2.使用ROWNUM进行分页查询 3.使用ROWNUM查看薪资前五位的员工 4.查询指定条数直接的数据 三、ROWNUM与ROWID不同 一、ROWID 伪列 表中的每一行在数据文件中都有一个物理地址&#xff0c;ROWID…

EMS只是快递那个EMS吗?它跟能源有什么关系?

在刚刚落幕的深圳人工智能终端展上&#xff0c;不少企业展示了与数字能源相关的技术和服务&#xff0c;其中一项关键系统——EMS&#xff08;Energy Management System&#xff0c;能量管理系统&#xff09;频频亮相。这个看似低调的名字&#xff0c;实际上正悄然成为未来能源管…

日志技术-LogBack、Logback快速入门、Logback配置文件、Logback日志级别

一. 日志技术 1. 程序中的日志&#xff0c;是用来记录应用程序的运行信息、状态信息、错误信息等。 2. JUL&#xff1a;(java.util.logging)这是JavaSE平台提供的官方日志框架&#xff0c;也被称为JUL。配置相对简单&#xff0c;但不够灵活&#xff0c;性能较差。 3.Logs4j&…

修改Cinnamon主题

~/.themes/Brunnera-Dark/cinnamon/cinnamon.css 1.修改 Tooltip 圆角大小&#xff0c;边框颜色&#xff0c;背景透明度 #Tooltip { border-radius: 10px; color: rgba(255, 255, 255, 0.8); border: 1px solid rgba(255, 255, 255, 0.6); background-color: rgba(0,…

91.评论日记

2025年5月30日20:27:06 AI画减速器图纸&#xff1f; 呜呜为什么读到机械博士毕业了才有啊 | 新迪数字2025新品发布会 | AI工业软件 | 三维CAD | 国产自主_哔哩哔哩_bilibili

HTML5实现简洁的端午节节日网站源码

HTML5实现简洁的端午节节日网站源码 前言一、设计来源1.1 网站首页界面1.2 端午由来界面1.3 节日活动界面1.4 传统美食界面1.5 民俗文化界面1.6 登录界面1.7 注册界面 二、效果和源码2.1 动态效果2.2 源代码 结束语 HTML5实现简洁的端午节节日网站源码&#xff0c;酷炫的大气简…

Window10+ 安装 go环境

一、 下载 golang 源码&#xff1a; 去官网下载&#xff1a; https://go.dev/dl/ &#xff0c;当前时间&#xff08;2025-05&#xff09;最新版本如下: 二、 首先在指定的磁盘下创建几个文件夹 比如在 E盘创建 software 文件夹 E:\SoftWare,然后在创建如下几个文件夹 E:\S…

一、Sqoop历史发展及原理

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月30日 专栏&#xff1a;Sqoop教程 在大数据时代&#xff0c;数据往往分散存储在各种不同类型的系统中。其中&#xff0c;传统的关系型数据库 (RDBMS) 如 MySQL, Oracle, PostgreSQL 等&#xff0c;仍然承载着大量的关键业务…

React 编译器 RC

&#x1f916; 作者简介&#xff1a;水煮白菜王&#xff0c;一位前端劝退师 &#x1f47b; &#x1f440; 文章专栏&#xff1a; 前端专栏 &#xff0c;记录一下平时在博客写作中&#xff0c;总结出的一些开发技巧和知识归纳总结✍。 感谢支持&#x1f495;&#x1f495;&#…

关于表连接

目录 1.左连接 2.右连接 3.内连接 4.全外连接 5.笛卡尔积 -- 创建表A CREATE TABLE A(PNO VARCHAR2(10) PRIMARY KEY, PAMT NUMBER, A_DATE DATE);-- 向表A插入数据 INSERT INTO A VALUES (01001, 100, TO_DATE(2005-01-01, YYYY-MM-DD)); INSERT INTO A VALUES (010…

【计算机网络】fork()+exec()创建新进程(僵尸进程及孤儿进程)

文章目录 一、基本概念1. fork() 系统调用2. exec() 系列函数 二、典型使用场景1. 创建子进程执行新程序2. 父子进程执行不同代码 三、核心区别与注意事项四、组合使用技巧1. 重定向子进程的输入/输出2. 创建多级子进程 五、常见问题与解决方案僵尸进程&#xff08;Zombie Proc…

Word表格怎样插入自动序号或编号

在Word文档中编辑表格时&#xff0c;经常需要为表格添加序号或编号&#xff0c;可以设置为自动序号或编号&#xff0c;当删除行时&#xff0c;编号会自动变化&#xff0c;不用手工再重新编号。如图所示。 序号数据1数据21300300230030033003004300300 一&#xff0c;建立word表…

无人机仿真环境(3维)附项目git链接

项目概述 随着无人机技术在物流、测绘、应急救援等领域的广泛应用&#xff0c;其自主导航、避障算法、路径规划及多机协同等核心技术的研究需求日益迫切。为降低实地测试成本、提高研发效率&#xff0c;本项目旨在构建一个高精度、可扩展的​​无人机三维虚拟仿真环境​​&…

Python 训练营打卡 Day 30-模块和库的导入

模块和库的导入 1.1标准导入 import mathprint("方式1: 使用 import math") print(f"圆周率π的值: {math.pi}") print(f"2的平方根: {math.sqrt(2)}\n") 1.2从库中导入特定项 from math import pi, sqrtprint("方式2&#xff1a;使用 f…

前端实现图片压缩:基于 HTML5 File API 与 Canvas 的完整方案

在 Web 开发中,处理用户上传的图片时,前端压缩可以有效减少服务器压力并提升上传效率。本文将详细讲解如何通过<input type="file">实现图片上传,结合 Canvas 实现图片压缩,并实时展示压缩前后的图片预览和文件大小对比。 一、核心功能架构 我们将实现以…

【Docker管理工具】部署Docker管理面板DweebUI

【Docker管理工具】部署Docker管理面板DweebUI 一、DweebUI介绍1.1 DweebUI 简介1.2 主要特点1.3 使用场景 二、本次实践规划2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载DweebUI镜像五、…

【后端高阶面经:架构篇】50、数据存储架构:如何改善系统的数据存储能力?

一、数据存储架构设计核心原则 (一)分层存储架构:让数据各得其所 根据数据访问频率和价值,将数据划分为热、温、冷三层,匹配不同存储介质,实现性能与成本的平衡。 热数据层:访问频率>100次/秒。采用Redis集群存储高频访问数据(如用户登录态、实时交易数据),配合…

Qt/C++开发监控GB28181系统/sip协议/同时支持udp和tcp模式/底层协议解析

一、前言说明 在gb28181-2011协议中&#xff0c;只有udp要求&#xff0c;从2016版本开始要求支持tcp&#xff0c;估计也是在多年的实际运行过程中&#xff0c;发现有些网络环境差的场景下&#xff0c;一些udp交互指令丢失导致功能异常&#xff0c;所以后面修订的时候增加了tcp…

晨控CK-FR03与汇川H5U系列PLC配置MODBUS TCP通讯连接操作手册

晨控CK-FR03与汇川H5U系列PLC配置MODBUS TCP通讯连接操作手册 CK-FR03-TCP是一款基于射频识别技术的高频RFID标签读卡器&#xff0c;读卡器工作频率为13.56MHZ&#xff0c;支持对I-CODE 2、I-CODE SLI等符合ISO15693国际标准协议格式标签的读取。 读卡器同时支持标准工业通讯协…