看完书上的链表还不会实现?不进来看看?
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)是一种二分类模型,它的基本模型是…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
