【数据结构】万字深入浅出讲解单链表(附原码 | 超详解)
创始人
2025-05-30 03:30:56

在这里插入图片描述

🚀write in front🚀
📝个人主页:认真写博客的夏目浅石.
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝
📣系列专栏:C语言实现数据结构
💬总结:希望你看完之后,能对你有所帮助,不足请指正!共同学习交流 🖊
✉️如果无聊的话,就来逛逛我的博客栈吧stack-frame.cn

文章目录

  • 前言
  • 一、链表概念
  • 二、链表的分类
    • 1. 单向或者双向
    • 2. 带头或者不带头
    • 3. 循环或者非循环
  • 三、链表的实现
    • 函数接口汇总SList.h
    • 函数接口实现SList.c
    • 代码实践Test.c
  • 代码汇总
  • 总结


前言

这几天看了数据结构的单链表这一节,真的收获很大,第一次看没有动手敲代码就是感觉学了和没学一样,今天也是从新又看了一遍,并且边学边敲代码,终于算是非常理解单链表这个东西了,今天就把我所学到的知识给大家分享一下。


一、链表概念

链表是一种物理存储结构非连续非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针
接次序实现的 。
在这里插入图片描述

二、链表的分类

1. 单向或者双向

在这里插入图片描述

2. 带头或者不带头

在这里插入图片描述

3. 循环或者非循环

在这里插入图片描述
虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构

1.无头单向链表

2.双向循环链表
在这里插入图片描述

  1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
  2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。

三、链表的实现

函数接口汇总SList.h


typedef int SLTDataType;typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SLTNode;//打印
void SLTPrint(SLTNode* phead);
SLTNode* BuySLTNode(SLTDataType x);
//增
void SLTPushBack(SLTNode** pphead,SLTDataType x);
void SLTPushFront(SLTNode** pphead,SLTDataType x);
//删
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);//查找
SLTNode* SListFind(SLTNode* phead,SLTDataType x);
//任意位置插入
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//删除某个节点
void SListErase(SLTNode** pphead, SLTNode* pos);
//在后面插入一个节点
void SListInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos后面这一个节点
void SListEraseAfter(SLTNode* pos);

函数接口实现SList.c


//--------------------------手动分割线---------------------------
//打印
void SLTPrint(SLTNode* phead)
{assert(phead);SLTNode* cur=phead;while(cur){printf("%d ",cur->data);cur=cur->next;}printf("NULL\n");
}
//--------------------------手动分割线-----------------------------
//创建新节点
SLTNode* BuySLTNode(SLTDataType x)
{SLTNode* newnode=(SLTNode*)malloc(sizeof(SLTNode));if(newnode==NULL) {printf("malloc fail\n");return NULL;}newnode->data=x;newnode->next=NULL;return newnode;
}
//--------------------------手动分割线-----------------------------
//尾插
void SLTPushBack(SLTNode** pphead,SLTDataType x)
{assert(pphead);SLTNode* newnode=BuySLTNode(x);if(*pphead==NULL){*pphead=newnode;}else{//找尾SLTNode* cur=*pphead;while(cur->next!=NULL){cur=cur->next;}cur->next=newnode;}
}
//--------------------------手动分割线-----------------------------
//前插
void SLTPushFront(SLTNode** pphead,SLTDataType x)
{assert(pphead);SLTNode* newnode=BuySLTNode(x);newnode->next=*pphead;*pphead=newnode;
}
//--------------------------手动分割线-----------------------------
//尾删
void SLTPopBack(SLTNode** pphead)
{// 暴力检查assert(pphead);assert(*pphead);SLTNode* cur=*pphead;if(*pphead->next==NULL){free(*pphead);*pphead=NULL;}else{SLTNode* cur=*pphead;while(cur->next->next!=NULL){cur=cur->next;}SLTNode* tmp=cur->next;free(tmp);cur->next=NULL;}
}
//--------------------------手动分割线-----------------------------
//前删
void SLTPopFront(SLTNode** pphead)
{assert(pphead);assert(*pphead);if(*pphead->next==NULL){free(*pphead);*pphead=NULL;}else{SLTNode* tmp=*pphead;free(tmp);tmp=NULL;*pphead=*pphead->next;}
}
//--------------------------手动分割线-----------------------------
//查找
SLTNode* SListFind(SLTNode* phead,SLTDataType x)
{SLTNode* cur=phead;while(cur){if(cur->data==x) return cur;cur=cur->next;}return NULL;
}
//--------------------------手动分割线-----------------------------
//任意位置插入
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pos);assert(pphead);SLTNode* newnode=BuySLTNode(x);if(*pphead==pos){SLTPushFront(pphead, x);}else{SLTNode* prev=*pphead;while(prev->next!=pos){prev=prev->next;}prev->next=newnode;newnode->next=pos;}
}
//--------------------------手动分割线-----------------------------
//删除某个节点
void SListErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(pos);SLTNode* cur=*pphead;if(*pphead==pos){SLTPopFront(pphead);}else{while(cur->next!=pos){cur=cur->next;}cur->next=pos->next;free(pos);pos=NULL;}
}
//--------------------------手动分割线-----------------------------
//在后面插入一个节点
void SListInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = BuySLTNode(x);newnode->next = pos->next;pos->next = newnode;
}
//--------------------------手动分割线-----------------------------
//删除pos后面这一个节点
void SListEraseAfter(SLTNode* pos)
{assert(pos);assert(pos->next);SLTNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}
//--------------------------手动分割线-----------------------------

代码实践Test.c

#include"SList.h"void TestSList1()
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);
}void TestSList2()
{SLTNode* plist = NULL;SLTPushFront(&plist, 1);SLTPushFront(&plist, 2);SLTPushFront(&plist, 3);SLTPushFront(&plist, 4);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);
}void TestSList3()
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);
}int main()
{TestSList3();return 0;
}

代码汇总

#include
#include
#includetypedef int SLTDataType;typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SLTNode;//--------------------------手动分割线---------------------------
//打印
void SLTPrint(SLTNode* phead)
{assert(phead);SLTNode* cur=phead;while(cur){printf("%d ",cur->data);cur=cur->next;}printf("NULL\n");
}
//--------------------------手动分割线-----------------------------
//创建新节点
SLTNode* BuySLTNode(SLTDataType x)
{SLTNode* newnode=(SLTNode*)malloc(sizeof(SLTNode));if(newnode==NULL) {printf("malloc fail\n");return NULL;}newnode->data=x;newnode->next=NULL;return newnode;
}
//--------------------------手动分割线-----------------------------
//尾插
void SLTPushBack(SLTNode** pphead,SLTDataType x)
{SLTNode* newnode=BuySLTNode(x);if(*pphead==NULL){*pphead=newnode;}else{//找尾SLTNode* cur=*pphead;while(cur->next!=NULL){cur=cur->next;}cur->next=newnode;}
}
//--------------------------手动分割线-----------------------------
//前插
void SLTPushFront(SLTNode** pphead,SLTDataType x)
{SLTNode* newnode=BuySLTNode(x);newnode->next=*pphead;*pphead=newnode;
}
//--------------------------手动分割线-----------------------------
//尾删
void SLTPopBack(SLTNode** pphead)
{//暴力检查assert(*pphead);SLTNode* cur=*pphead;if(*pphead->next==NULL){free(*pphead);*pphead=NULL;}else{SLTNode* cur=*pphead;while(cur->next->next!=NULL){cur=cur->next;}SLTNode* tmp=cur->next;free(tmp);cur->next=NULL;}
}
//--------------------------手动分割线-----------------------------
//前删
void SLTPopFront(SLTNode** pphead)
{assert(*pphead);if(*pphead->next==NULL){free(*pphead);*pphead=NULL;}else{SLTNode* tmp=*pphead;free(tmp);tmp=NULL;*pphead=*pphead->next;}
}
//--------------------------手动分割线-----------------------------
//查找
SLTNode* SListFind(SLTNode* phead,SLTDataType x)
{SLTNode* cur=phead;while(cur){if(cur->data==x) return cur;cur=cur->next;}return NULL;
}
//--------------------------手动分割线-----------------------------
//任意位置插入
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{SLTNode* newnode=BuySLTNode(x);if(*pphead==pos){SLTPushFront(pphead, x);}else{SLTNode* prev=*pphead;while(prev->next!=pos){prev=prev->next;}prev->next=newnode;newnode->next=pos;}
}
//--------------------------手动分割线-----------------------------
//删除某个节点
void SListErase(SLTNode** pphead, SLTNode* pos)
{SLTNode* cur=*pphead;if(*pphead==pos){SLTPopFront(pphead);}else{while(cur->next!=pos){cur=cur->next;}cur->next=pos->next;free(pos);pos=NULL;}
}
//--------------------------手动分割线-----------------------------
//在后面插入一个节点
void SListInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = BuySLTNode(x);newnode->next = pos->next;pos->next = newnode;
}
//--------------------------手动分割线-----------------------------
//删除pos后面这一个节点
void SListEraseAfter(SLTNode* pos)
{assert(pos);assert(pos->next);SLTNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}
//--------------------------手动分割线-----------------------------void TestSList1()
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);
}void TestSList2()
{SLTNode* plist = NULL;SLTPushFront(&plist, 1);SLTPushFront(&plist, 2);SLTPushFront(&plist, 3);SLTPushFront(&plist, 4);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);
}void TestSList3()
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);
}int main()
{TestSList3();return 0;
}

总结

  今天学习了单链表的知识,初次写数据结构的知识,给我的感觉就是,学三遍不如手敲代码一遍来的实在,所以数据结构的学习我将多画图,多敲代码来学习,希望大家吸取经验和我一起学习数据结构,为后面打比赛刷题打下坚实基础。

  我是夏目浅石,希望和你一起学习进步,刷题无数!!!希望各位大佬能一键三连支持一下博主,hhhh~我们下期见喽
在这里插入图片描述
如果无聊的话,就来逛逛我的博客栈吧stack-frame.cn

✨原创不易,还希望各位大佬支持一下\textcolor{blue}{原创不易,还希望各位大佬支持一下}原创不易,还希望各位大佬支持一下

👍 点赞,你的认可是我创作的动力!\textcolor{9c81c1}{点赞,你的认可是我创作的动力!}点赞,你的认可是我创作的动力!

⭐️ 收藏,你的青睐是我努力的方向!\textcolor{ed7976}{收藏,你的青睐是我努力的方向!}收藏,你的青睐是我努力的方向!

✏️ 评论,你的意见是我进步的财富!\textcolor{98c091}{评论,你的意见是我进步的财富!}评论,你的意见是我进步的财富!

相关内容

热门资讯

汽车产业已经有“恒大”了?价格... 中国汽车产业即将迎来重大考验!近日,以直率著称的长城汽车董事长魏建军在接受媒体专访时,发出惊人警告:...
九牧的三重跃升:新场景、新智造... AI时代,中国卫浴加速征战世界。文 | 华商韬略 熊剑辉当上过春晚的宇树人形机器人,化身“礼仪小姐姐...
限流|限流算法 一、概念限流顾名思义,就是对请求或并发数进行限制;通过对一个时间窗口内的...
浙江金融反腐风暴:工行浙江分行... 靴子落地。5月30日,据中央纪委国家监委驻中国工商银行纪检监察组、辽宁省纪委监委消息:中国工商银行浙...
四年前收购的一家公司,如今要上... 这位实控人四年前收购了个企业,现在就准备让公司在港交所上市了!5月29日,港交所官网披露,江苏日御光...
美的集团董事长方洪波:小米进入... 5月30日,美的集团(000333.SZ、00300.HK,简称美的)董事长兼总裁方洪波在美的集团2...
产品研发利好引发连日大涨 舒泰... 新京报贝壳财经讯(记者丁爽)5月30日,舒泰神再度大涨收场。至当日收盘,公司股价涨15.02%,报2...
sql 标准的隔离级别 事务并发执行时遇到的一致性问题脏读(Dirty Read)如果一个事务读...
【读书笔记】电子商务 目录1,四种业务模式2,三种电商模式2.1 运营模式:“流...
5月收官,个股平均涨5%!机构... A股5月收官,全月大盘维持震荡,概念题材热点飞速轮动。今年的5月并不是“5穷”,两市5000余只个股...
说到底,钱生钱就这4个办法|小... 点击 “简七读财” ,发送消息“理财小工具”免费领取40个赚钱工具资源包~晚上好呀~不少朋友吐槽,...
一杯咖啡3块钱,“外卖补贴”带... 本文来源:时代周报 作者:孙艺格 图源:库迪咖啡官网1元的超大杯奶茶、1.68元的美式咖啡、2.6...
中国联塑创始人之子上任执行董事... 一则人事变动,让国内管道建材头部企业中国联塑集团控股有限公司(以下简称“中国联塑”)受到关注。中国联...
谷粒学院SpringSecur... 登录功能前端分析前端会调用此接口去实现登录// 登录export function login(us...
【基于协同过滤算法的推荐系统项... 本文目录1、推荐系统的关键元素1.1 数据1.2 算法1.3 业务领域1.4 展示信息2、推荐算法的...
公告精选丨中国交建:拟以5亿元... 今日焦点中国交建:拟以5亿元-10亿元回购公司A股股份中国交建公告称,公司拟不低于5亿元,不超过10...
javascript数组常用方... 数组对于程序语言的重要性自不必多说,而在使用javascript的时候难免也会需要用到...
SpringBoot学习--基... 本专栏主要记录SpringBoot学习之路 文章目录1 SpringBoot基本介绍1.1 官方文档...
重组停牌!渤海汽车拟购海纳川旗... 5月30日,渤海汽车(600960.SH)发布公告称,公司拟通过发行股份及支付现金的方式,购买北京海...
Spring学习(五) 事物管理: 一、事物管理的回顾: 1、事物的概念: 事物&...
java线程同步 并发:同一个对象被多个线程同时操作处理多线程问题时,多个线程访问同一个对...
代码审计(二) 一、DevSecOps的概念DevSecOps 是描述开发、安全和运营集成的术语。它是一种文化、自动...
西安高新“楼市新政”,一场“教... 第 2302期〡2025/05/30西安各区域的土地市场和区域价值或将重新洗牌。上周,西安高新区一份...
603023,下周复牌!正式摘... 2025.05.30本文字数:855,阅读时长大约2分钟5月30日,*ST威帝公告称,公司股票将于2...
第十章 STM32+ESP82... 前言 最近有不少小伙伴私信留言,想要我推出一章能够通过APP进行远程控制并获取传感器信...
试题 算法训练 逗志芃的暴走 问题描述   逗志芃是有妹子的现充,但是有时候妹子就是烦恼。因为逗志芃太逗了ÿ...
德邦证券董事会改组完成,山东财... 山东财金集团披露,5月29日,德邦证券在济南召开2025年度第一次临时股东会,会议选举产生公司新一届...
法网:郑钦文2-0胜资格赛黑马... 北京时间5月30日,2025赛季网球大满贯法国公开赛继续进行,在女单第三轮的一场比赛中,赛会8号种子...
宗馥莉接手父亲名下娃哈哈实业公... 天眼查App显示,近日,浙江娃哈哈实业股份有限公司发生工商变更,宗庆后卸任法定代表人、董事长、总经理...
北交所上市公司恒拓开源大宗交易... 每经讯,2025年5月30日,北交所上市公司恒拓开源(834415,收盘价:17.83元)发生一笔大...