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

单链表<数据结构 C版>

目录

概念

 链表的单个结点

 链表的打印操作

新结点的申请

尾部插入

头部插入

尾部删除

头部删除

查找

在指定位置之前插入数据

在任意位置之后插入数据

测试运行一下:

 删除pos结点

 删除pos之后结点

 销毁链表


概念

单链表是一种在物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接顺序实现的。

链表的每个结点有两个部分,分别是数据和指向下个结点的指针,每个链表的最后一个结点的下一个结点为NULL(不能对NULL解引用)。

放一张bit课件里的图,我觉得很形象:


 链表的单个结点

typedef int SLDataType;//重定义一下在链表内存放的数据类型,方便后期对类型进行统一修改//链表的单个结点
typedef struct SListNode {//Single List Node :链表结点SLDataType data;//存放的数据struct SListNode* next;//指向下一个结点的指针
}SLNode;//重定义名字方便后期使用

 链表的打印操作

//链表的打印操作
void SLPrint(SLNode* phead) {assert(phead);//不能传入空指针SLNode* pcur = phead;//pointer cursor:指针光标,不让头结点丢失(虽然不会改变头结点的指向)while (pcur) {//等同于pcur!=NULLprintf("%d->", pcur->data);//打印此结点的数据pcur = pcur->next;//使pcur指向下一个结点}printf("NULL\n");
}

新结点的申请

后面会涉及到新结点的插入,申请新结点可以封装成一个函数,避免代码冗余

//新结点的申请
SLNode* SLBuyNode(SLDataType x) {SLNode* node = (SLNode*)malloc(sizeof(SLNode));if (!node) {//返回值为空,申请失败(一般是空间不足了),直接退出perror("malloc fail");exit(1);}node->data = x;//将数据赋给datanode->next = NULL;//将下一个节点置为NULL;return node;
}

尾部插入

//尾部插入
void SLPushBack(SLNode** pphead, SLDataType x) {//注意在这里我们传参的是二级指针,因为我们需要在函数内部改变头结点的指向assert(pphead);//不能传NULL//新结点的申请SLNode* node=SLBuyNode(x);if (*pphead == NULL) {*pphead = node;}else {SLNode* pcur = *pphead;while (pcur->next) {//找到next元素为NULL的结点,也就是为链表尾部pcur = pcur->next;}pcur->next = node;}}

测试运行一下:


头部插入

//头部插入
void SLPushFront(SLNode** pphead, SLDataType x) {assert(pphead);SLNode* node = SLBuyNode(x);//这里需要处理头结点为空的情况吗?不需要,因为没有涉及到解引用其元素node->next = *pphead;*pphead = node;
}

测试运行一下:


尾部删除

//尾部删除
void SLPopBack(SLNode** pphead) {assert(*pphead && pphead);//if (!(*pphead)->next) {//处理只有一个元素的情况free(*pphead);*pphead = NULL;}else {SLNode* prev = NULL;SLNode* pcur = *pphead;while (pcur->next) {//找到尾结点前一个结点prev = pcur;pcur = pcur->next;}free(pcur);//将尾结点释放pcur = NULL;prev->next = NULL;//将尾结点的前一个结点的next元素置为NULL}
}

测试运行一下:


头部删除

//头部删除
void SLPopFront(SLNode** pphead) {assert(*pphead && pphead);SLNode* next = (*pphead)->next;//保存头结点的下一个结点地址free(*pphead);//释放头结点*pphead = next;//使头结点指向下一个结点
}

测试运行一下:


查找

在链表中想要对指定位置进行操作不能使用下标,所以我们必须找到指定位置的地址才能对其进行操作。

//查找
SLNode* SLFind(SLNode* phead, SLDataType x) {assert(phead);SLNode* pcur = phead;while (pcur) {if (pcur->data == x) {return pcur;}pcur = pcur->next;}return NULL;
}

在指定位置之前插入数据

//在指定位置之前插入数据
void SLInsert(SLNode** pphead, SLNode* pos, SLDataType x) {assert(pphead && pos && *pphead);//pos和pphead不能为空,以及pphead指向空间不能为空if (*pphead == pos) {//处理头插的情况SLPushFront(pphead,x);}else {SLNode* node = SLBuyNode(x);SLNode* pcur = *pphead;while (pcur->next != pos) {//找到pos前一个结点pcur = pcur->next;}node->next = pcur->next;pcur->next = node;}
}

测试运行一下:


在任意位置之后插入数据

//在任意位置之后插入数据
void SLInsertAfter(SLNode* pos, SLDataType x) {assert(pos);//pos不能为空SLNode* node = SLBuyNode(x);node->next = pos->next;pos->next = node;
}

测试运行一下:


 删除pos结点

//删除pos结点
void SLErase(SLNode** pphead, SLNode* pos) {assert(*pphead && pphead && pos);if (*pphead == pos) {//处理头删的情况SLPopFront(pphead);}else {SLNode* pcur = *pphead;while (pcur->next!= pos) {pcur = pcur->next;}pcur->next = pos->next;free(pos);pos = NULL;}
}

 测试运行一下:


 删除pos之后结点

//删除pos之后结点
void SLEraseAfter(SLNode* pos) {assert(pos && pos->next);//pos后面的结点也不能为空SLNode* next = pos->next;pos->next = next->next;free(next);next = NULL;
}

 测试运行一下:

 


 销毁链表

//销毁链表
void SLDestory(SLNode** pphead) {assert(pphead && *pphead);SLNode* pcur = *pphead;while (pcur) {SLNode* nextnode = pcur->next;//使用一个变量接受下一个结点地址free(pcur);pcur = nextnode;}*pphead = NULL;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Ubantu 使用 docker 配置 + 远程部署 + 远程开发
  • 【JavaScript 算法】贪心算法:局部最优解的构建
  • JVM(day2)经典垃圾收集器
  • C++:类的定义和实例化
  • 云微客如何实现低成本快速获客?AI矩阵来传播
  • Linux-交换空间(Swap)管理
  • 第三章 OSPF高级
  • JuiceFS缓存特性
  • GitHub私有派生仓库(fork仓库) | 派生仓库改为私有
  • 尚硅谷大数据技术-数据湖Hudi视频教程-笔记03【Hudi集成Spark】
  • uni-app学习HBuilderX学习-微信开发者工具配置
  • 前端Canvas入门——用canvas写五子棋?
  • 【python学习】爬虫中常使用的urllib和requests库的的背景、定义、特点、功能、代码示例以及两者的区别
  • 数据结构小测试:排序算法
  • OpenCv 如何在 Java 中使用
  • [deviceone开发]-do_Webview的基本示例
  • CSS实用技巧
  • HTTP那些事
  • java8 Stream Pipelines 浅析
  • javascript从右向左截取指定位数字符的3种方法
  • Transformer-XL: Unleashing the Potential of Attention Models
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • 搭建gitbook 和 访问权限认证
  • 代理模式
  • 服务器从安装到部署全过程(二)
  • -- 数据结构 顺序表 --Java
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • 7行Python代码的人脸识别
  • Python 之网络式编程
  • 国内开源镜像站点
  • ​configparser --- 配置文件解析器​
  • ​iOS安全加固方法及实现
  • ###C语言程序设计-----C语言学习(3)#
  • #Linux(帮助手册)
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (二)springcloud实战之config配置中心
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (六)激光线扫描-三维重建
  • (十)c52学习之旅-定时器实验
  • (十三)MipMap
  • (淘宝无限适配)手机端rem布局详解(转载非原创)
  • (转)linux自定义开机启动服务和chkconfig使用方法
  • (转)winform之ListView
  • (转)清华学霸演讲稿:永远不要说你已经尽力了
  • ./configure,make,make install的作用(转)
  • .java 9 找不到符号_java找不到符号
  • .mp4格式的视频为何不能通过video标签在chrome浏览器中播放?
  • .net MVC中使用angularJs刷新页面数据列表
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .net遍历html中全部的中文,ASP.NET中遍历页面的所有button控件
  • .NET设计模式(8):适配器模式(Adapter Pattern)
  • //解决validator验证插件多个name相同只验证第一的问题
  • /var/log/cvslog 太大
  • ??myeclipse+tomcat