当前位置: 首页 > article >正文

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

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.lryc.cn/news/2393043.html

相关文章:

  • 编码总结如下
  • 《算力觉醒!ONNX Runtime + DirectML如何点燃Windows ARM设备的AI引擎》
  • [9-1] USART串口协议 江协科技学习笔记(13个知识点)
  • Oracle基础知识(五)——ROWID ROWNUM
  • 简述synchronized和java.util.concurrent.locks.Lock的异同 ?
  • OpenCV CUDA模块直方图计算------在 GPU 上计算图像直方图的函数calcHist()
  • EMS只是快递那个EMS吗?它跟能源有什么关系?
  • 日志技术-LogBack、Logback快速入门、Logback配置文件、Logback日志级别
  • 修改Cinnamon主题
  • 91.评论日记
  • HTML5实现简洁的端午节节日网站源码
  • Window10+ 安装 go环境
  • AWS WebRTC:获取ICE服务地址(part 2): ICE Agent的作用
  • 一、Sqoop历史发展及原理
  • React 编译器 RC
  • PyTorch 中mm和bmm函数的使用详解
  • 关于表连接
  • 【计算机网络】fork()+exec()创建新进程(僵尸进程及孤儿进程)
  • QPS 和 TPS 详解
  • Word表格怎样插入自动序号或编号
  • 数据结构:导论
  • 青少年编程与数学 02-020 C#程序设计基础 13课题、数据访问
  • 无人机仿真环境(3维)附项目git链接
  • 湖北理元理律师事务所:债务优化中的“生活锚点”设计
  • Python 训练营打卡 Day 30-模块和库的导入
  • 前端实现图片压缩:基于 HTML5 File API 与 Canvas 的完整方案
  • 【Docker管理工具】部署Docker管理面板DweebUI
  • 【后端高阶面经:架构篇】50、数据存储架构:如何改善系统的数据存储能力?
  • 编程之巅:语言的较量
  • STM32 通过 ESP8266 通信详解