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

《数据结构学习笔记---第三篇》---单链表具体实现

目录

1.链表

1.1 链表的概念及结构

2.不带头单链表的实现

 2.1创建头文件“SList.h”

2.2 创建具体接口实现文件SList.c

2.2.1打印

2.2.2申请链表结点

2.2.3创建一个长度为n的链表

 2.2.4尾插尾删

2.2.5头插头删

2.2.6寻找x元素,返回pos

2.2.7插入和删除pos之后的位置

 2.2.8插入pos之前的位置和删除pos位置

2.2.9 销毁链表

3.主函数的实现



1.链表

1.1 链表的概念及结构

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

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

1. 单向、双向

2. 带头、不带头

3. 循环、非循环

虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:

1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结 构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向 循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而 简单了,后面我们代码实现了就知道了。

2.不带头单链表的实现

 2.1创建头文件“SList.h”

  ​​​​​为什么要创立头文件

#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>typedef int SLTDataType;
typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SLTNode;SLTNode* BuySLTNode(SLTDataType);//申请结点SLTNode* CreateSList(int n);//创建一个多长的链表void SLTPrint(SLTNode* phead);void SLTPushBack(SLTNode**pphead,SLTDataType x);
void SLTPopBack(SLTNode**pphead);
void SLTPushFront(SLTNode** pphead, SLTDataType x);
void SLTPopFront(SLTNode** pphead);SLTNode* SListFind(SLTNode* plist, SLTDataType x);//找x的位置void SListInsertAfter(SLTNode* pos, SLTDataType x);//插入pos之后的位置
void SListErasetAfter(SLTNode* pos);//删除pos后面的位置void SListInsert(SLTNode**pphead,SLTNode* pos, SLTDataType); //插入pos之前的位置 
void SLTErase(SLTNode** pphead,SLTNode* pos);//删除posvoid SLTDestroy(SLTNode** pphead);//销毁链表

这里我们用来结构体的嵌套来定义单链表的节点结构体的嵌套定义

2.2 创建具体接口实现文件SList.c

先引用#include "SList.h"

2.2.1打印

void SLTPrint(SLTNode* phead)
{SLTNode* cur = phead;/*if (cur == NULL){printf("NULL");}*/while (cur){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}

 在这里我们没有断言assert,如果头指针为空指针,程序就会打印出NULL。

2.2.2申请链表结点

SLTNode* BuySLTNode(SLTDataType x)//申请结点
{SLTNode* newnode= (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("melloc fail");exit(-1);}newnode->data = x;newnode->next = NULL;return newnode;
}

 在这里我们用了扩容,但我们使用的是malloc,和顺序表有所不同的是我们并不需要异地扩容,对于链表来说存储的位置本就是随机的,不需要整块连续的空间。

2.2.3创建一个长度为n的链表

SLTNode* CreateSList(int n)//创建一个多长的链表
{SLTNode* phead = NULL, * ptail = NULL;int x = 0;for (int i = 0 ; i < n; i++){SLTNode* newnode = BuySLTNode(i);if (phead == NULL){phead = ptail = newnode;}else{ptail->next = newnode;ptail = newnode;}}return phead;
}

 2.2.4尾插尾删

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{SLTNode* cur =*pphead;SLTNode* newnode = BuySLTNode(x);if (*pphead == NULL){*pphead =newnode;}else {while (cur->next){cur = cur->next;}cur->next = newnode;}
}void SLTPopBack(SLTNode** pphead)
{assert(*pphead);SLTNode* tail = *pphead;SLTNode* prev = *pphead;if ((* pphead)->next == NULL){free(*pphead);*pphead = NULL;}else {while (tail->next->next){tail = tail->next;}free(tail->next);tail->next = NULL;}
}

 注意:

  • 我们可以看到我们这里与顺序表明显不同的是我们这里传入了二级指针,这到底是为什么,看这篇博文单链表尾插过程中为什么传入二级指针
  • 尾删时注意 free(tail)与 free(tail->next)的区别,其实原理和上述问题类似,因为后者修改的是结构体指针直接改变到了结构体,而仅仅free(tail)会导致出了函数的作用域,tail栈帧销毁,无法真正的改变到结构体

2.2.5头插头删

void SLTPushFront(SLTNode** pphead, SLTDataType x)
{SLTNode* newnode = BuySLTNode(x);newnode->next = *pphead;*pphead = newnode;}void SLTPopFront(SLTNode** pphead) {assert(*pphead);SLTNode* cur = *pphead;cur = cur->next;free(*pphead);*pphead = cur;}

2.2.6寻找x元素,返回pos

SLTNode* SListFind(SLTNode* plist, SLTDataType x)
{assert(plist);SLTNode* cur = plist;while (cur){if (cur->data == x){return cur;}cur = cur->next;}return NULL;}//找x的位置

2.2.7插入和删除pos之后的位置

void SListInsertAfter(SLTNode* pos, SLTDataType x)
{/*if (pos == NULL){SLTPushBack(pos, x);}else {*/assert(pos);SLTNode*newnode=BuySLTNode(x);SLTNode* cur = pos;SLTNode* lnext = pos->next;cur->next = newnode;newnode->next = lnext;/*}*/
}//插入pos之后的位置}

 2.2.8插入pos之前的位置和删除pos位置

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) {assert(pos);if (*pphead == pos){SLTPushFront(pphead, x);}else{SLTNode* prev = *pphead;SLTNode* newnode = BuySLTNode(x);/*while (prev->next){if (prev->next == pos){prev->next = newnode;newnode->next = pos;}prev = prev->next;}*/while (prev->next->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;}} //插入pos之前的位置void SLTErase(SLTNode** pphead, SLTNode* pos)//删除pos
{assert(pos);//写错了if (pos== *pphead){SLTPopFront(pphead);}else {SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}SLTNode* lnext = pos;prev->next = prev->next->next;free(pos);}
}//删除pos

2.2.9 销毁链表

void SLTDestroy(SLTNode** pphead)
{SLTNode* cur = *pphead;while (cur){SLTNode* tail = cur->next;free(cur);cur = tail;cur = cur->next;}*pphead = NULL;
}//销毁链表

注意置空,防止野指针,打印的时候没断言。

3.主函数的实现

#include "SList.h"
void SListTest1() {SLTNode* n1 = BuySLTNode(1);SLTNode* n2 = BuySLTNode(2);SLTNode* n3 = BuySLTNode(3);SLTNode* n4 = BuySLTNode(4);SLTNode* n5 = BuySLTNode(5);n1->next = n2;n2->next = n3;n3->next = n4;n4->next = n5;n5->next = NULL;
//	SLTNode*plist=CreateSList(5);SLTPrint(n1);
}
void SListTest2() {SLTNode* plist = CreateSList(1);SLTPrint(plist);/*SLTPushBack(&plist, 100);*/SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);SLTPushFront(&plist,9);SLTPushFront(&plist,99);SLTPushFront(&plist,999);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);
}
void SListTest3() {SLTNode* plist = CreateSList(5);SLTPrint(plist);/*SLTNode*p=SListFind(plist, 3);SListInsertAfter(p, 100);SLTPrint(plist);SListErasetAfter(p);SLTPrint(plist);*/SLTNode*p = SListFind(plist, 0);/*SListInsert(&plist, p, 999);SLTPrint(plist);*/SLTErase(&plist, p);SLTPrint(plist);SLTDestroy(&plist);SLTPrint(plist);
}int main()
{SListTest1();SListTest2();SListTest3();return 0;
}

相关文章:

  • 提升JavaScript代码质量的最佳实践
  • 2024最新华为OD机试试题库全 -【幼儿园圆桶的取出顺序】- C卷
  • LNMP架构之mysql数据库实战
  • easyExcel大数据量导出oom
  • [BT]BUUCTF刷题第9天(3.27)
  • 整数的反转
  • 离线数仓(八)【DWD 层开发】
  • 芯片工程系列(5)2.5D 3D封装
  • 13 Games101 - 笔记 - 光线追踪(Whitted-Style光线追踪原理详解及实现细节)
  • docker日志大小设置(doker logs)
  • Spring_MVC
  • IP如何异地共享文件?
  • Spring实战:采用Spring配置文件管理Bean
  • 项目搭建之统一返回值
  • 【机器学习】包裹式特征选择之序列前向选择法
  • CODING 缺陷管理功能正式开始公测
  • CSS选择器——伪元素选择器之处理父元素高度及外边距溢出
  • Java精华积累:初学者都应该搞懂的问题
  • Laravel深入学习6 - 应用体系结构:解耦事件处理器
  • Logstash 参考指南(目录)
  • Node项目之评分系统(二)- 数据库设计
  • Redux 中间件分析
  • SpringCloud(第 039 篇)链接Mysql数据库,通过JpaRepository编写数据库访问
  • TCP拥塞控制
  • 诡异!React stopPropagation失灵
  • 将 Measurements 和 Units 应用到物理学
  • 老板让我十分钟上手nx-admin
  • 让你的分享飞起来——极光推出社会化分享组件
  • 通信类
  • 进程与线程(三)——进程/线程间通信
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • $.proxy和$.extend
  • (23)Linux的软硬连接
  • (4.10~4.16)
  • (Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
  • (Redis使用系列) Springboot 实现Redis消息的订阅与分布 四
  • (二)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (机器学习-深度学习快速入门)第一章第一节:Python环境和数据分析
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • (转)程序员技术练级攻略
  • (转载)CentOS查看系统信息|CentOS查看命令
  • *_zh_CN.properties 国际化资源文件 struts 防乱码等
  • .htaccess 强制https 单独排除某个目录
  • .MSSQLSERVER 导入导出 命令集--堪称经典,值得借鉴!
  • .NET LINQ 通常分 Syntax Query 和Syntax Method
  • .net反编译工具
  • /deep/和 >>>以及 ::v-deep 三者的区别
  • [ 隧道技术 ] cpolar 工具详解之将内网端口映射到公网
  • [ 云计算 | AWS 实践 ] Java 如何重命名 Amazon S3 中的文件和文件夹
  • [20180224]expdp query 写法问题.txt
  • [Android]通过PhoneLookup读取所有电话号码
  • [Angular] 笔记 21:@ViewChild
  • [Asp.net MVC]Bundle合并,压缩js、css文件
  • [AutoSar NVM] 存储架构
  • [C#]C# winform部署yolov8目标检测的openvino模型