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

深入浅出链表

目录

1.链表的基本概念及结构

1.1基本概念

1.2结构

2.链表的分类

3.链表的实现(循环链表增删查改实现)

1.动态申请节点(结点)​编辑

2.单链表打印

3.单链表尾插

4.单链表头插

5.单链表尾删

6.单链表头删

7.单链表查找

8.在指定位置之前插入数据

9.在指定位置之后插入数据

10.删除pos节点

11.删除pos之后的节点

12.销毁链表


1.链表的基本概念及结构

        链表是一种常见的基础数据结构,它由一系列节点(Node)组成,每个节点包含数据和到下一个节点的引用(指针)。以下是链表的基本概念及其结构:

1.1基本概念

  1. 节点(Node):链表的基本单元,每个节点包含两部分,一部分是存储数据的域(称为数据域),另一部分是存储下一个节点地址的域(称为指针域或链接域)。

  2. 头节点(Head):链表的第一个节点,通常用于表示整个链表。

  3. 尾节点(Tail):链表的最后一个节点,其指针域通常指向NULL,表示链表的结束。

  4. 链表的长度:链表中节点的数量。

  5. 链表的遍历:按照节点间的链接顺序访问链表中的每个节点。

  6. 动态性:链表的大小不是固定的,可以在运行时动态地增加或减少节点。

1.2结构

        在C语言中,链表节点通常使用结构体(struct)来定义。以下是链表节点的一个基本结构:

链表的结构就像是火车一样一节一节的连在一起。

但是每个节点的地址并不像顺序表一样是连续的,而是随机存储的,因此每个节点才需要下一个节点的地址来找到下一节点。

2.链表的分类

  1. 单向链表(Singly Linked List):

    • 每个节点包含一个数据域和一个指向下一个节点的指针。
    • 遍历链表只能从头节点开始,并且只能向一个方向进行。
  2. 双向链表(Doubly Linked List):

    • 每个节点包含一个数据域、一个指向前一个节点的指针和一个指向下一个节点的指针。
    • 可以从两个方向遍历链表。
  3. 循环链表(Circular Linked List):

    • 单向链表的变种,最后一个节点的指针指向头节点,形成一个环。
    • 可以从任意节点开始遍历整个链表。
  4. 双向循环链表(Doubly Circular Linked List):

    • 双向链表的变种,头节点的前一个指针指向最后一个节点,最后一个节点的下一个指针指向头节点,形成一个环。
    • 可以从任意节点开始,向前或向后遍历整个链表。

3.链表的实现(循环链表增删查改实现)

1.动态申请节点(结点)

  • SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));:使用malloc函数动态分配一个SLTNode大小的内存块,并将其强制转换为SLTNode*类型的指针。sizeof(SLTNode)获取SLTNode结构体的大小。

  • if (newnode == NULL):检查malloc是否成功分配了内存。如果newnodeNULL,说明内存分配失败。

  • perror("malloc fail!");:如果内存分配失败,使用perror函数打印错误消息。

  • exit(1);:如果内存分配失败,终止程序执行,返回错误代码1

  • newnode->data = x;:将传入的参数x赋值给新节点的数据域。

  • newnode->next = NULL;:将新节点的指针域初始化为NULL,表示当前节点后面没有其他节点。

  • return newnode;:返回新创建的节点。

2.单链表打印

3.单链表尾插

  • assert(pphead);:使用assert宏来确保传入的头节点指针的指针不是NULL。如果ppheadNULL,程序将终止执行。

  • SLTNode* newnode = SLTBuyNode(x);:调用之前定义的SLTBuyNode函数来创建一个新的节点,并将数据x存储在新节点的数据域。

  • if (*pphead == NULL):检查链表是否为空。如果链表为空(即头节点指针为NULL),则将新节点设置为头节点。

  • else:如果链表不为空,则需要找到链表的最后一个节点。

  • SLTNode* ptail = *pphead;:初始化一个指针ptail,指向头节点。

  • while (ptail->next):遍历链表,直到找到最后一个节点(即节点的next指针为NULL)。

  • ptail = ptail->next;:在循环中,将ptail移动到下一个节点。

  • ptail->next = newnode;:当找到最后一个节点时,将其next指针指向新创建的节点,从而将新节点添加到链表的末尾。

4.单链表头插

  • assert(pphead);:使用assert宏来确保传入的pphead不是NULL。如果ppheadNULL,程序将在这里终止。

  • SLTNode* newnode = SLTBuyNode(x);:调用SLTBuyNode函数创建一个新的节点,并将数据x存储在新节点的数据域中。

  • newnode->next = *pphead;:将新节点的next指针指向原来的头节点。这一步是为了将新节点插入到链表的头部。

  • *pphead = newnode;:更新头节点指针,使其指向新创建的节点。这样,新节点就成为了链表的新头部。

5.单链表尾删

这个接口的实现一开始先要判断链表是否为单节点链表,如果是则直接释放头节点,不是则进行相应的操作。

  • assert(pphead && *pphead);:使用assert宏来确保传入的pphead不是NULL,且链表不为空。如果这些条件不满足,程序将在这里终止。

  • if ((*pphead)->next == NULL):检查链表是否只有一个节点。如果是,直接释放这个节点,并将头节点指针设置为NULL

  • else:如果链表有多个节点,需要遍历链表找到最后一个节点。

  • while (ptail->next):遍历链表直到ptail指向最后一个节点。

  • free(ptail);:释放最后一个节点的内存。

  • ptail = NULL;:将ptail指针设置为NULL,防止野指针。

  • prev->next = NULL;:将倒数第二个节点的next指针设置为NULL,断开与已删除节点的连接。

6.单链表头删

这里的(*pphead)->next不能写成*pphead->next 因为->预算符优先级是高于*的。

  • assert(pphead && *pphead);:使用assert宏来确保传入的pphead不是NULL,且链表不为空。如果这些条件不满足,程序将在这里终止。

  • SLTNode* next = (*pphead)->next;:保存头节点的下一个节点指针。这是因为一旦释放头节点,我们就无法访问链表的其余部分。

  • free(*pphead);:释放头节点的内存。

  • *pphead = next;:更新头节点指针,使其指向原来的第二个节点(现在成为新的头节点)。

7.单链表查找

  • assert(phead);:使用assert宏来确保传入的phead不是NULL。如果pheadNULL,程序将在这里终止。

  • SLTNode* plist = phead;:初始化一个指针plist,使其指向头节点。

  • while (plist):开始循环,当plist不为NULL时继续执行。

  • if (plist->data == x):检查当前节点的数据是否与要查找的值x匹配。

  • return plist;:如果找到匹配的节点,返回该节点。

  • plist = plist->next;:如果没有找到匹配的节点,移动plist到下一个节点。

  • return NULL;:如果在链表中没有找到匹配的节点,返回NULL

8.在指定位置之前插入数据

9.在指定位置之后插入数据

  • assert(pphead && *pphead);:使用assert宏来确保传入的pphead不是NULL,且链表不为空。如果这些条件不满足,程序将在这里终止。

  • assert(pos);:使用assert宏来确保传入的pos不是NULL。如果posNULL,程序将在这里终止。

  • SLTNode* newnode = SLTBuyNode(x);:调用SLTBuyNode函数创建一个新的节点,并将数据x存储在新节点的数据域中。

  • if (pos == *pphead):检查插入的位置是否是头节点。如果是,使用SLTPushFront函数在头节点位置插入新节点。

  • else:如果插入的位置不是头节点,需要找到pos的前一个节点。

  • SLTNode* prev = *pphead;:初始化prev指针指向头节点。

  • while (prev->next != pos):遍历链表直到找到pos的前一个节点。

  • newnode->next = pos;:将新节点的next指针指向pos

  • prev->next = newnode;:将pos的前一个节点的next指针指向新节点。

10.删除pos节点

  • assert(pphead && *pphead);:使用assert宏来确保传入的pphead不是NULL,且链表不为空。如果这些条件不满足,程序将在这里终止。

  • assert(pos);:使用assert宏来确保传入的pos不是NULL。如果posNULL,程序将在这里终止。

  • if (pos == *pphead):检查要删除的节点是否是头节点。如果是,使用SLTPopFront函数执行头删操作。

  • else:如果要删除的节点不是头节点,需要找到该节点的前一个节点。

  • SLTNode* prev = *pphead;:初始化prev指针指向头节点。

  • while (prev->next != pos):遍历链表直到找到pos的前一个节点。

  • prev->next = pos->next;:将pos的前一个节点的next指针指向pos的下一个节点,完成删除操作。

  • free(pos);:释放pos节点的内存。

  • pos = NULL;:将pos指针设置为NULL,防止野指针。

11.删除pos之后的节点

  • assert(pos);:使用assert宏来确保传入的pos不是NULL。如果posNULL,程序将在这里终止。

  • SLTNode* newnode = SLTBuyNode(x);:调用SLTBuyNode函数创建一个新的节点,并将数据x存储在新节点的数据域中。

  • newnode->next = pos->next;:将新节点的next指针指向pos的下一个节点,这样新节点就位于pos的后面。

  • pos->next = newnode;:将posnext指针指向新节点,完成新节点的插入操作。

12.销毁链表

  • assert(pphead && *pphead);:使用assert宏来确保传入的pphead不是NULL,且链表不为空。如果这些条件不满足,程序将在这里终止。

  • SLTNode* pcur = *pphead;:初始化一个指针pcur,使其指向头节点。

  • while (pcur):开始循环,当pcur不为NULL时继续执行。

  • SLTNode* next = pcur->next;:保存pcur的下一个节点的指针,因为pcur将被释放。

  • free(pcur);:释放pcur节点的内存。

  • pcur = next;:移动pcur指针到下一个节点。

  • *pphead = NULL;:循环结束后,将头指针设置为NULL,表示链表已被销毁。

相关文章:

深入浅出链表

目录 1.链表的基本概念及结构 1.1基本概念 1.2结构 2.链表的分类 3.链表的实现(循环链表增删查改实现) 1.动态申请节点(结点)​编辑 2.单链表打印 3.单链表尾插 4.单链表头插 5.单链表尾删 6.单链表头删 7.单链表查找 …...

Linux核心命令入门

Linux常用命令 文件管理文件目录管理文件查看编辑 系统管理网络管理hostnamehost/nslookuptraceroutenetstat列出所有端口 (包括监听和未监听的)列出所有处于监听状态的 Sockets显示每个协议的统计信息 硬件管理df(Disk Free)du(Disk Usage&a…...

腾讯无界微前端框架介绍

一、无界微前端框架概述 无界微前端框架是由腾讯团队推出的,旨在解决现有微前端方案中存在的问题,如适配成本高、样式隔离困难、运行性能不佳、页面白屏、子应用通信复杂、子应用保活机制缺乏等。 技术实现 无界微前端的核心技术是基于Web Component…...

Linux——网络(2)

一、通信 --- 不同主机上进程间的通信 1、IP和端口号 IP:标识网络中的一台主机 本质上 32位的整型数据 端口号: 标识某个进程 本质上 16位的整型数据 2、udp和tcp udp的特点: 1.无连接 2.不可靠 tcp的特点: 1.面…...

结合量子技术解决数据传输安全

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 接前篇:数采网关面…...

【Rust光年纪】提高开发效率:深入了解Rust语言中的数据库客户端和文件处理库

深入探索:Rust语言中多款数据库客户端与文件处理库详解 前言 在现代软件开发中,使用各种数据库和文件处理操作是非常常见的。Rust语言作为一种快速、安全、并发的系统编程语言,也拥有丰富的生态系统和库。本文将介绍几个用于Rust语言的数据…...

【自动驾驶】控制算法(一)绪论与前期准备

写在前面: 🌟 欢迎光临 清流君 的博客小天地,这里是我分享技术与心得的温馨角落。📝 个人主页:清流君_CSDN博客,期待与您一同探索 移动机器人 领域的无限可能。 🔍 本文系 清流君 原创之作&…...

CSDN创作一周年总结

一周年总结 文章目录 一周年总结我的第一篇文章这一年我收获到了什么?1.培养了逻辑能力2.形成了自己的知识库,知识网络3.功利性的收获 我的第一篇文章 不知不觉之间,也已经过去一年了。还记得第一次决定在csdn上写博客,是因为进入…...

World of Warcraft [CLASSIC] the Eye of Eternity [EOE] P1-P2

World of Warcraft [CLASSIC] the Eye of Eternity [EOE] 永恒之眼(蓝龙) 第一阶段 第二阶段 第三阶段 载具1-6技能介绍 World of Warcraft [CLASSIC] the Eye of Eternity [EOE]_永恒之眼 eoe-CSDN博客 永恒之眼怎么出副本呢,战斗结束&am…...

一键翻译全球:多语言支持下的英文翻译工具

随着科技的飞速发展,一系列英文翻译工具应运而生,它们以人工智能为驱动,极大地简化了跨语言交流的过程。本文将带您一窥英文翻译工具探索那些能够帮助我们跨越语言鸿沟的神奇工具。 1.福昕在线翻译 链接直达>>https://fanyi.pdf365.c…...

水战再起波澜,“怡宝”要下好怎样一盘棋?

不少投资者常把那些刚需性强、永远也不可能淘汰的产业称为“日不落产业”,从细分板块来看,水无疑具有一定代表性。农夫山泉掌门人钟晱晱曾直言:“我选择了一个日不落的产业,你永远要喝水,不可能不喝水。” 多年下来&a…...

使用maven快速生成打包文件3

这里再介绍一种打包方式&#xff0c;依赖包分开打包&#xff0c;直接将需要部署的文件打包成一个要锁文件&#xff0c;比如kafka-roma-bin.tar.gz&#xff0c;这里需要两个文件&#xff0c;一个pom2.xml&#xff0c;一个package.xml。 pom2.xml <?xml version"1.0&q…...

Excel技巧(一)

快捷键技巧 原文链接 选取某一行的数据直到最后一行&#xff1a;【CTRL SHIFT ↓ 】或者选取一行后按住SHIFT键&#xff0c;双击下边线就可以快速选取区域。 如果表格中有多行空行&#xff0c;可以先按CTRL SHIFT END&#xff0c;再按CTRL SHIFT 上下键调整&#xff0c;…...

C语言:文件复制

文本文件复制&#xff1a; #include<stdio.h>int main() {FILE* pFile1 NULL;FILE* pFile2 NULL;fopen_s(&pFile1,"D:\\11111.txt","r");fopen_s(&pFile2,"D:\\222.txt", "w");char c;while((cfgetc(pFile1))!EOF){f…...

谈谈建筑项目管理:类型、流程和工具

无论是在材料采购还是供应商管理方面&#xff0c;确保建筑项目按计划进行并控制在预算内始终是一项挑战。 如今&#xff0c;建筑项目管理正逐步采用软件驱动的方法来提升其效率。这一转型显著优化了项目规划、调度和资源配置&#xff0c;使建筑管理更加精确和高效。 什么是建…...

【Vue】生命周期函数

系列文章目录 第五章 生命周期函数 文章目录 系列文章目录 生命周期函数代表的是Vue实例&#xff0c;或者是Vue组件&#xff0c;在网页中各个生命阶段所执行的函数。生命周期函数可以分为创建阶段、挂载阶段、更新阶段以及卸载阶段。 创建阶段&#xff1a;setup挂载阶段&…...

C++系列-文件操作

文件操作 文件类型文本文件二进制文件 文件操作的三大类文件的打开方式ios::app(append)和 ios::ate(at end) 写文件写文件文件步骤读文件文件步骤二进制文件读写写一般数据写特殊数据 程序运行时产生的数据都属于临时数据&#xff0c;一旦程序运行完毕&#xff0c;就会释放&am…...

ES6解构赋值详解;全面掌握:JavaScript解构赋值的终极指南

目录 全面掌握&#xff1a;JavaScript解构赋值的终极指南 一、数组解构赋值 1、基本用法 2、跳过元素 3、剩余元素 4、默认值 二、对象解构赋值 1、基本用法 2、变量重命名 3、默认值 4、嵌套解构 三、复杂的嵌套结构解构 四、函数参数解构赋值 1、对象解构作为函…...

2-73 基于matlab的weber能量法求解齿轮时变啮合刚度的程序

基于matlab的weber能量法求解齿轮时变啮合刚度的程序&#xff0c;能够跑出刚度图&#xff0c;通过求解轮齿部分变形、基体变形及局部接触变形这三部分的变形&#xff0c;进而求得综合弹性变形&#xff0c;最终求出时变啮合刚度。程序已调通&#xff0c;可直接运行。 2- 73 齿轮…...

[C++]set和map的介绍及使用

关于set和map的接口函数部分&#xff0c;只重点介绍一些相较于别的容器有特殊地方的接口&#xff0c;set和map的接口可以触类旁通。 一、概念 &#xff08;一&#xff09;、关联式容器 关联式容器存储的元素是一个个的键值对<key,value>。通过键&#xff08;key&#x…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent

安全大模型训练计划&#xff1a;基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标&#xff1a;为安全大模型创建高质量、去偏、符合伦理的训练数据集&#xff0c;涵盖安全相关任务&#xff08;如有害内容检测、隐私保护、道德推理等&#xff09;。 1.1 数据收集 描…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...

32位寻址与64位寻址

32位寻址与64位寻址 32位寻址是什么&#xff1f; 32位寻址是指计算机的CPU、内存或总线系统使用32位二进制数来标识和访问内存中的存储单元&#xff08;地址&#xff09;&#xff0c;其核心含义与能力如下&#xff1a; 1. 核心定义 地址位宽&#xff1a;CPU或内存控制器用32位…...