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

单链表专题

前言

顺序表和单链表都是两种常见的数据结构,他们的区别究竟在哪里?

顺序表:顺序表是一种连续的存储结构,数据元素在内存中占据一块连续的空间。因为连续空间存储的特性,顺序表可以直接通过下表遍历元素。

单链表:单链表是一种离散的存储结构,数据元素存储在节点中,节点中包含数据和下一节点的指针。

总结:顺序表和单链表的区别在于他们的存储方式不同,顺序表是连续存储,单链表则是离散存储,单链表相对于顺序表的优势是,单链表可以降低操作时的时间复杂度。

1.链表的概念及结构

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

链表的结构就像这个小火车,每一个车厢就是一个节点,节点包括数据(车厢里的货物)和下一个节点的指针(车厢之间的抓钩)。

在链表中的小火车就像这样

每个节点对应的结构体代码就可以这样写出(假设当前保存的节点为整型)

typedef struct SListNode { int data; //节点数据 struct SListNode* next; //指针变量 用来保存下一个节点的地址 }SLTNode;
我们想要保存⼀个数据时,实际是向操作系统申请了⼀块内存,这个内存不仅要保存数
据,也需要保存下⼀个节点的地址(当下⼀个节点为空时保存的地址为空)。
当我们想要从第⼀个节点⾛到最后⼀个节点时,只需要在前⼀个节点拿上下⼀个节点的地址就可以了。
那在链表结构中,如何实现节点从头到尾的打印?
void SLTPrint(SLTNode* phead){ SLTNode *pcur = phead; while(pcur) { printf("%d ",pcur->data); pcur = pcur->next; } printf("\n"); }

测试样例:

void SlistTest01(){ SLTNode* node1 = (SLTNode* )malloc(sizeof(SLTNode)); node1->data = 1; SLTNode* node2 = (SLTNode* )malloc(sizeof(SLTNode)); node1->data = 2; SLTNode* node3 = (SLTNode* )malloc(sizeof(SLTNode)); node1->data = 3; SLTNode* node4 = (SLTNode* )malloc(sizeof(SLTNode)); node1->data = 4; //此时创建好了节点数据 但还没有实现节点链接 node1-> = node2; node2-> = node3; node3-> = node4; node4-> = NULL; } void SLTPrint(SLTNode* phead){ SLTNode *pcur = phead; while(pcur) { printf("%d ",pcur->data); pcur = pcur->next; } printf("NULL\n"); } SLTNode* plist = node1; SLtPrint(plist);

测试结果

2.单链表的实现

2.1单链表的尾插

typedef char SLTDataType; SLTNode* SLTBuyNode(SLTDataType x) { SLTNode* newnode = (SLTNode* )malloc(sizeof(SLTNode)); if(newnode == NULL) { perror("malloc fail!"); exit(1); } newnode->data = x; newnode->next = NULL; return newnode; } void SLTPushBack(SLTNode** pphead,SLTDataType x){ assert(pphead); SLTNode* newnode = SLTBuyNode(x); //链表为空 新节点为phead if(*pphead == NULL) { *pphead = newnode; return; } //链表不为空 找尾结点 //为了不改变头结点 设置临时结构体变量 SLTNode* ptail = *pphead; while(ptail->next) { ptail = ptail->next; } //ptail就是尾结点 ptail->next = newnode; }

测试样例:

void SlistTest02() { SLTnode* plist = NULL; SLTPushback(&plist,1); SLTPushback(&plist,2); SLTPushback(&plist,3); SLTPushback(&plist,4); SLTPrint(plist); //结果 1->2->3->4->NULL }

运行结果

2.2单链表的头插

void SLTPushFront(SLTNode** pphead,SLTDataType x) { assert(pphead); SLTNode* newnode = SLTBuyNode(x); new->next=*pphead; *pphead = newnode; }

测试样例

void SlistTest03(){ SLTPushFront(&plist,5); SLTPrint(plist); SLTPushFront(&plist,6); SLTPrint(plist); SLTPushFront(&plist,7); SLTPrint(plist); }

结果

7->6->5->NULL

2.3 链表的头删和尾删

//单链表的尾删 void SLTPopBack(SLTNode** pphead){ assert(pphead); //pphead链表不能为空 *phead首节点也不能为空 assert(*pphead); //链表不为空 //链表只有一个节点,有多个节点 if((*pphead)->next == NULL) { free(*pphead); *pphead = NULL; return; } SLTNode* ptail = *pphead; SLTNode* prev = NULL; //存放前区节点 while(ptail->next) { prev = ptail; ptail = ptail->next; } prev->next = NULL; //销毁尾结点 free(ptail); ptail = NULL; } //单链表的头删 void SLTPopFront(SLTNode** pphead) { assert(pphead); assert(*pphead); //链表不能为空 //让第二个节点成为新的头 同时把旧的头结点释放掉 SLTNode* next = (*pphead)->next; free(*pphead); *pphead = next; } //测试 SLTPopBack(&plist); SLTPrint(plist); SLTPopFront(&plist); SLTPrint(plist);

2.4 链表的查找

SLTNode* SLTFind(SLTNode** pphead,SLTDataType x) { assert(pphead); //遍历链表 SLTNode* pcur = *pphead; while(pcur) //等价于pcur != NULL { if(pcur->data == x){ return pcur;} } //没有找到 return NULL; } //测试 SLTNode* FindRet = SLTFind(&plist); if(FindRet) { printf("找到了!"); } else{ printf("未找到!"); }

2.5 在指定位置之前插入数据

void SLTInsert(SLTNode** pphead,SLTNode* pos,SLTDataType x){ assert(pphead); assert(pos); assert(*pphead); //链表也不能为空 因为传入的指定节点也不能为空 SLTNode* newnode = SLTBuyNode(x); //当pos刚好是头结点时 if(pos == *pphead) { SLTPushFront(pphead,x);/头插 return; } //以下为pos不是头结点的情况 SLTNode* prev = *pphead; while(prev->next!=pos) { prev = prev->next; } } //测试 SLTNode* FindRet = SLTFind(&plist,4); SLTInsert(&plist,FindRet,100); SLTPrint(plist);

2.6 在指定位置之后插入数据

void SLTInsertAfter(SLTNode* pos,SLTDataType x) { assert(pos); //传入节点不能为空 SLTNode* newnode = SLTBuyNode(x); //创建新节点 newnode->next = pos->next; //先接新节点后 再接新节点前 pos->next = newnode; } //测试 void SlistTest03(){ SLTNode* plist = NULL; SLTPushBack(&plist,1); SLTPushBack(&plist,2); SLTPushBack(&plist,3); SLTPushBack(&plist,4); SLTPrint(plist); //1->2->3->4->NULL SLTNode* FindRet = SLTFind(&plist,1); SLTInsertAfter(FindRet,100); SLTPrint(plist); //1->100->2->3->4->NULL }
http://www.cnnetsun.cn/news/4174745.html

相关文章:

  • 从awesome-python3-webapp到iOS App:跨平台开发实战经验完整指南
  • Xplorer文件管理器完全上手指南:跨平台文件管理一次搞定
  • 告别手写提示词:llava-v1.6-mistral-7b-hf 的 apply_chat_template 完整使用指南
  • Stronghold Procedures 完全参考:BIP39、SLIP10、Ed25519 签名等 20+ 密码学操作一览
  • orga分词器源码剖析:基于text-kit读取器的lexer逐行设计解读
  • 免费打造FIFA 23梦想球队:Live Editor 生涯模式修改器完整上手指南
  • 流媒体时代,本地音乐播放器如何以“简洁”定义核心价值?
  • 工业具身智能落地的工程基石:从概念到实战的系统化底座构建指南
  • Qt插件机制详解:QPluginLoader动态加载与模块化架构完整指南(Awesome_Qt_Learning)
  • 为什么你的Redis客户端太慢?异步Redis客户端aredis完整概览
  • 铸铁平台与钢结构平台选型对比:从阻尼特性到全生命周期成本分析
  • XUnity.AutoTranslator:十分钟让没中译的 Unity 游戏跑起来
  • Connect You 开源联系人应用开发者指南:Jetpack Compose + Room 架构完整解析
  • 从斯大林排序算法看算法正确性与数据完整性
  • 猫抓 Cat-Catch:免费的网页视频资源嗅探与流媒体下载扩展
  • BBDown 命令行下载器:一条命令把 B 站视频存成本地 MP4
  • DRF Docs 安全指南:HIDE_DOCS 配置全解,为什么生产环境必须隐藏 API 文档
  • 多元分数多项式为何衰落?从统计建模稳定性与机器学习范式演变谈起
  • gogstash源码解析(三):codec编解码机制与simpleQueue队列暂停恢复的背压设计
  • SceneKit节点克隆与材质独立难题:Shinkansen 3D Seat Booking Prototype的NodeFactory深克隆技巧
  • 美赛微分方程建模实战:从识别到求解的完整指南
  • rack-tracker 埋点中间件安全深度解析:从 XSS 防护到线程安全的完整设计指南
  • 腾讯前端面试核心考点:JS基础与框架原理解析
  • LÖVE Potion架构深度剖析:modules/objects/utilities三层设计,LÖVE框架移植方法论全解读
  • TP6-Vue-Admin:ThinkPHP6 后台 + Vue 管理后台,前后端分离后台管理系统快速搭建指南
  • 分布感知算法设计:LLM智能体如何根据数据特征优化算法性能
  • 数学建模实战:从数据清洗到趋势预测,解析全球变暖问题的数据科学方法论
  • 突破大数据处理瓶颈:Awesome Data Analysis收录20个高性能工具,Polars、Dask一网打尽
  • 美团大模型产品岗面试全解析:技术考察与业务场景
  • KeplerMapper Cover类深度讲解:n_cubes与perc_overlap如何决定图的精细度