看完书上的链表还不会实现?不进来看看?
1.1链表的概念
定义:
链表是一种物理存储上非连续,数据元素的逻辑顺序通过链表中的指针链接次序,实现的一种线性存储结构。
特点:
链表由一系列节点组成,节点在运行时动态生成 (malloc),每个节点包括两个部分:
一个是存储数据元素的数据域
另一个是存储下一个节点地址的指针域
如图:

1.2链表的概述
链表作为 C 语言中一种基础的数据结构,在平时写程序的时候用的并不多,但在操作系统里面使用的非常多。不管是RTOS还是Linux等使用非常广泛,所以必须要搞懂链表,链表分为单向链表和双向链表,单向链表很少用,使用最多的还是双向链表。单向链表懂了双向链表自然就会了。
1.3单向不带头不循环的链表的初始化
参数的传入:涉及改变链表的操作统统用指针传链表(其中特别注意的是需要改变传入的头结点时需要传入二级指针)不然函数调用完成之后,为传入的链表分配的的内存会自动释放,链表不会有任何改变。创建头结点,为头结点分配内存。
令头节点的指针为空指针(指针不初始化容易出现很多问题)
PS:这里为什么要动态分配内存呢?
因为线性表的顺序存储结构用数组实现,而数组占用的是一整块内存,数组元素分布很集中,需要提前预定数组的长度。而链表是一种动态结构,它所占用的大小和位置是不需要提前分配的,可以根据自身的需求即时生成。
第二步:创建第二个节点,将其放在第一个节点的后面(第一的节点的指针域保存第二个节点的地址)
第三步:再次创建节点,找到原本链表中的最后一个节点,接着讲最后一个节点的指针域保存新节点的地址,以此内推。
1.4单链表的一些操作
1.4.1链表的遍历
1.输出第一个节点的数据域,输出完毕后,让指针保存后一个节点的地址
2.输出移动地址对应的节点的数据域,输出完毕后,指针继续后移
3.以此类推,直到节点的指针域为NULL。
1.4.2链表的查找
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x) {assert(plist);SListNode* cur = plist;while (cur){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}
1.4.3链表的头插头删,尾插尾删
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{if (*pplist == NULL){SListNode* newnode = BuySListNode(x);*pplist = newnode;}else{SListNode *newnode = BuySListNode(x);newnode->next=*pplist;*pplist = newnode;}
}
// 单链表头删
void SListPopFront(SListNode** pplist)
{assert(*pplist);if ((*pplist)->next == NULL)//对结构体指针的地址传值时访问该结构体的成员先得对结构体地址解引用{SListNode* cur = *pplist;//直接free(*pplist),会使得pplist被置为随机值free(cur);*pplist = NULL;}else{SListNode *cur = *pplist;*pplist = (*pplist)->next;//左右两边的星号都不要漏。free(cur);}
}// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{if (*pplist == NULL){SListNode* newnode = BuySListNode(x);*pplist = newnode;}else{SListNode* tail = *pplist;SListNode* newnode = BuySListNode(x);while (tail->next){tail = tail->next;}tail->next = newnode;}
}
// 单链表的尾删
void SListPopBack(SListNode** pplist) {assert(*pplist);SListNode* tail=*pplist,*prev=*pplist;if (tail->next == NULL){free(tail);*pplist = NULL;}else {while (tail->next){prev = tail;tail = tail->next;}prev->next = NULL;free(tail);tail = NULL;}
}
1.4.4单链表的插入
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?因为找pos之前的位置需要O(n)的时间复杂度
void SListInsertAfter(SListNode* pos, SLTDateType x) {assert(pos);SListNode* newnode = BuySListNode(x);newnode->next = pos->next;pos->next = newnode;
}
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos) {assert(pos&&pos->next);SListNode* next = pos->next;pos->next = next->next;free(next);next = NULL;
}
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
因为找pos之前的位置需要O(n)的时间复杂度进行查找到pos的前一个元素
1.4.5链表节点的删除
如果链表为空,不需要删除
如果删除的是第一个结点,则需要将保存链表首地址的指针保存第一个结点的下一个结点的 地址
如果删除的是中间结点,则找到中间结点的前一个结点,让前一个结点的指针域保存这个结 点的后一个结点的地址即可
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos) {assert(pos&&pos->next);SListNode* next = pos->next;pos->next = next->next;free(next);next = NULL;
}
1.4.6单链表的销毁
重新定义一个指针q,保存p指向节点的地址,然后p后移保存下一个节点的地址,然后释放q对应的节点,以此类推,直到p为NULL为止。
// 单链表的销毁
void SListDestroy(SListNode* plist) {while (plist){SListNode* next = plist;free(plist);plist = next;}
}
1.5双向带头不循环的链表
虽然听起来复杂,但其实只是结构复杂,但是遍历还是增删查改插入和删除等都是十分简单的
#include"List.h"ListNode* BuyListNode(int x)
{ListNode* new = (ListNode*)malloc(sizeof(ListNode));new->data = x;new->next = NULL;new->prev = NULL;return new;
}
// 创建返回链表的头结点.
ListNode* ListCreate(void)
{ListNode* newhead = (ListNode*)malloc(sizeof(ListNode));newhead->next = newhead;newhead->prev = newhead;return newhead;
}
// 双向链表销毁
void ListDestory(ListNode* pHead)
{assert(pHead);ListNode* cur = pHead;while (cur){ListNode* next = cur->next;free(cur);cur = next;}
}
// 双向链表打印
void ListPrint(ListNode* pHead)
{assert(pHead);ListNode* cur = pHead->next;while (cur != pHead){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* tail = pHead->prev;ListNode* new = BuyListNode(x);new->next = tail->next;tail->next->prev = new;new->prev = tail;tail->next = new;
}
// 双向链表尾删
//需要注意此处如果链表初始化后不断删除会使得pHead指针指向的地方不确定,如果后续用户未重新创造双向链表的哨兵位而继续插入数据,会导致非法访问。
//或者加个条件判断判断是否只有一个哨兵位,是的话就不再进行删除。
void ListPopBack(ListNode* pHead)
{assert(pHead);ListNode* tail = pHead->prev;tail->prev->next= tail->next;//左边修改的是指针域,右边是具体的链表的位置。tail->next->prev = tail->prev;free(tail);//tail = NULL;
}
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{ListInsert(pHead->next, x);
}
// 双向链表头删
void ListPopFront(ListNode* pHead)
{assert(pHead);ListNode* cur = pHead->next;if (cur != pHead){pHead->next = cur->next;cur->next->prev = pHead;free(cur);}cur = NULL;
}
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x) {assert(pHead);ListNode* cur = pHead->next;while (cur != pHead){if (cur->data == x){return cur;}cur = cur->next;}if (cur == pHead){return NULL;}
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x) {assert(pos);ListNode *new=BuyListNode(x);ListNode* prev = pos->prev;new->next = pos;pos->prev = new;new->prev = prev;prev->next = new;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos) {ListNode* prev = pos->prev;prev->next = pos->next;pos->next->prev = prev;free(pos);}
着重注意这段
// 双向链表尾删
//需要注意此处如果链表初始化后不断删除会使得pHead指针指向的地方不确定,如果后续用户未重新创造双向链表的哨兵位而继续插入数据,会导致非法访问。
//或者加个条件判断判断是否只有一个哨兵位,是的话就不再进行删除。
void ListPopBack(ListNode* pHead)
{
assert(pHead);
ListNode* tail = pHead->prev;
tail->prev->next= tail->next;//左边修改的是指针域,右边是具体的链表的位置。
tail->next->prev = tail->prev;
free(tail);
//tail = NULL;
}
相关文章:

看完书上的链表还不会实现?不进来看看?
1.1链表的概念定义:链表是一种物理存储上非连续,数据元素的逻辑顺序通过链表中的指针链接次序,实现的一种线性存储结构。特点:链表由一系列节点组成,节点在运行时动态生成 (malloc),…...
【批处理脚本】-3.2-call命令详解
"><--点击返回「批处理BAT从入门到精通」总目录--> 共5页精讲(列举了所有call的用法,图文并茂,通俗易懂) 在从事“嵌入式软件开发”和“Autosar工具开发软件”过程中,经常会在其集成开发环境IDE(CodeWarrior,S32K DS,Davinci,EB Tresos,ETAS…)中,…...

华为OD机试题,用 Java 解【寻找相同子串】问题
华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典使用说明 参加华为od机试,一定要注意不…...

思腾合力深思系列 | 四款高性能 AI 服务器
深思系列 AI 服务器涵盖多种 CPU 平台,支持按客户需求预装 OS、驱动、DL 框架、常用 DL 库,节省您大量的前期调试时间,开机即用。 一个简单的任务,若想要在 AI 的脑中形成清晰的思路,需要大量的实验和练习。从 AI 训练…...

Vue3做出B站【bilibili】 Vue3+TypeScript+ant-design-vue【快速入门一篇文章精通系列(一)前端项目案例】
本项目分为二部分 1、后台管理系统(用户管理,角色管理,视频管理等) 2、客户端(登录注册、发布视频) Vue3做出B站【bilibili】 Vue3TypeScriptant-design-vue【快速入门一篇文章精通系列(一&…...
2.3操作系统-进程管理:死锁、死锁的产生条件、死锁资源数计算
2.3操作系统-进程管理:死锁、死锁的产生条件、死锁资源数计算死锁死锁的产生条件死锁资源数计算死锁 进程管理是操作系统的核心,如果设计不当,就会出现死锁的问题。如果一个进程在等待意见不可能发生的事,进程就会死锁。而如果一…...
人物百科怎么建?个人百度百科创建的注意事项
百科词条根据百科类型可分为人物词条、品牌词条以及企业词条等等,对于不同类型的词条,在创建时有着不同的规则要求。 相对于品牌词条和企业词条,人物词条是相对有难度的一类,因为品牌有注册商标,企业有营业执照,都是比较容易佐证的…...

ArrayList与LinkedList的区别 以及 链表理解
list接口中ArrayList、LinkedList都不是线程安全,Vector是线程安全 1、数据结构不同 ArrayList是Array(动态数组)的数据结构,LinkedList是Link(链表)双向链表的数据结构。 2、空间灵活性 ArrayList最好指定初始容量 LinkedList是比ArrayList灵活的&a…...

电脑蓝屏怎么办?这5个技巧你必须学会
案例:电脑蓝屏是什么原因?怎么样可以解决? “救命!!!电脑是怎么了?开机直接蓝屏,是哪里坏了吗?前几天电脑还是好的,今早一打开就是蓝屏,可能是之…...

大数据 | (三)centos7图形界面无法执行yum命令
大家好,今天是三八女神节了! 你知道吗?世界上第一位电脑程序设计师是名女性,Ada Lovelace (1815-1852)。 她是一位英国数学家兼作家,第一位主张计算机不只可以用来算数的人,也发表了第一段分析机用的演算…...

历史上被发现的第一个真正的Bug - Grace Hopper
写在前面:博主是一只经过实战开发历练后投身培训事业的“小山猪”,昵称取自动画片《狮子王》中的“彭彭”,总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域,如今终有小成…...

KiCad 编译
KiCad 编译 因为最新项目需要,所以看了一下KiCad的编译,这里介绍的是64位电脑的编译,32位小伙伴请绕道官网看教程呦。 您可以在KiCad内查看基本的编译教程。 我这里也是参考的官网编译教程进行的编译,接下来让我们一起看看吧。…...

HTML 简介
文章目录HTML 简介实例解析什么是HTML?HTML 标签HTML 元素Web 浏览器HTML 网页结构HTML版本<!DOCTYPE> 声明通用声明HTML5HTML 4.01XHTML 1.0中文编码HTML 简介 HTML 实例 <!DOCTYPE html> <html><head><meta charset"utf-8"><ti…...
2023浙江省赛“信息安全管理与评估“--数字取证调查--网络数据包分析解析(高职组)
2022全国职业技能大赛“信息安全管理与评估”(高职组)任务书 2022全国职业技能大赛“信息安全管理与评估”任务书第一阶段竞赛项目试题第二阶段竞赛项目试题任务 2: 网络数据包分析第三阶段竞赛项目试题2022全国职业技能大赛“信息安全管理与评估”任务书 第一阶段竞赛项目…...

【Redis应用】查询缓存相关问题解决(二)
🚗Redis应用学习第二站~ 🚩起始站:【Redis应用】基于Redis实现共享session登录(一) 🚩本文已收录至专栏:Redis技术学习 👍希望您能有所收获,底部附有完整思维导图 一.概述 本篇我们会一起来学习…...

【SpringCloud】SpringCloud教程之Nacos实战(三集群配置)
目录前言一.Nacos集群逻辑图二.Nacos集群搭建1.搭建数据库,初始化数据库表结构2.下载Nacos3.配置Nacos3.启动Nacos4.配置启动nginx5.测试是否成功6.设置服务的nacos地址7.新增一个配置,查看数据看是否进行持久化了前言 在我前面两篇讲的都是单个nacos&a…...

什么是激励能力?HR人才测评
什么是激励能力?激励能力主要是针对管理型岗位而言的,尤其是团队型管理,既要督导团队成员,更需要掌握激励下属的方法和技巧。在HR人才测评系统中,对于管理型岗位的人才测评指标,通常也会包含激励能力&#…...

【刷题笔记】之滑动窗口(长度最小的子数组、水果成篮、最小的覆盖子串)
滑动窗口模板//滑动窗口模板:注意使用滑动窗口方法,使用一个 for(while) 循环中的变量是用来控制终止位置的//最小滑窗:给定数组 nums,定义滑动窗口的左右边界 i、j,求满足某个条件的滑窗的最小长度 for(j 0; j < …...

【JavaScript速成之路】JavaScript函数
📃个人主页:「小杨」的csdn博客 🔥系列专栏:【JavaScript速成之路】 🐳希望大家多多支持🥰一起进步呀! 文章目录前言1,函数基础1.1,函数概念1.2,函数使用1.3&…...

萤火虫算法优化SVM变压器故障分类预测,fa-svm分类预测,libsvm参数优化
目录 支持向量机SVM的详细原理 SVM的定义 SVM理论 Libsvm工具箱详解 简介 参数说明 易错及常见问题 SVM应用实例,基于fa-svm分类预测 代码 结果分析 展望 支持向量机SVM的详细原理 SVM的定义 支持向量机(support vector machines, SVM)是一种二分类模型,它的基本模型是…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...

【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...