hello! 各位宝子们大家好啊!今天给大家带来的是初阶数据结构中单链表的构建方式,手把手教会你单链表!
⛳️链表是指一种逻辑上是连在一起的数据结构,但是物理存储上却是分开的部分!是通过链表中的指针链接次序实现的一种数据结构!
本期文章收录在《初阶数据结构》,大家有兴趣可以看看呐!
⛺️ 欢迎铁汁们 ✔️ 点赞 收藏 ⭐留言 !
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
顺序表我们都知道有点类似数组,是在物理上一块连续存放的内存块!所以顺序表也叫 线性表 但是开辟必须需要连续的空间空间的浪费特别严重!
- 所以就有链表这种数据结构,避免了空间的浪费。
- 链表在逻辑上是连在一起但是内存块确实分布在不同位置的
- 通过指针访问每个链表的节点
链表在逻辑上是连续的但是,物理上是单独分开的。由每一块的指针记录下一个节点的地址进行访问
注意
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
但是我们今天就先从简单的入手,先来实现一下单链表的结构!先从简单的下手。
- 单链表结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
好了废话不多讲,下面我们就来到链表的实现过程。链表前面我们了解了不是一个连续的存储结构,S是利用指针来进行每个节点的访问。
链表链表首先我们要先定义链表的结构,链表既有数据又要和下一个链表联系起来那么肯定是要使用结构体:
typedef int SLTDataType;
//定义链表结构
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
单链表要实现的功能其实也非常简单,和顺序表是一模一样的。增删改查等这些操作而我们只要把这些函数实现了,那么在刷关于链表的题的时候也就无非是这些操作的变形。
//动态申请链表节点
SLTNode* BuySListNode(SLTDataType x);
//打印单链表
void SLTPrint(SLTNode* plist);
//单链表头插
void SLTPushFront(SLTNode** pplist,SLTDataType x);
//单链表尾插
void SLTPushBack(SLTNode** pplist, SLTDataType x);
//单链表头删
void SLTPopFront(SLTNode** pplist);
//单链表尾删
void SLTPopBack(SLTNode** pplist);
//查找节点
SLTNode* SLTFind(SLTNode* pplist, SLTDataType x);
//在pos以前插入x
void SLTInsert(SLTNode** pplist, SLTNode* pos, SLTDataType x);
// 在pos以后插入x
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
// 删除pos位置
void SLTErase(SLTNode** pplist, SLTNode* pos);
// 删除pos的后一个位置
void SLTEraseAfter(SLTNode* pos);
// 单链表的销毁
void SListDestroy(SLTNode* pplist);
前面我们把链表的基本结构定义好了那么接下来就申请节点了,这样我们才能进行链表的链接已经数据的存储!
//动态申请链表节点
SLTNode* BuySListNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc file");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
打印单链表也是一个十分简单的功能了,既然我们链表的每个节点的 next 存储的都是下一个节点那么直接循环访问就好啦!
//打印单链表
void SLTPrint(SLTNode* plist)
{
SLTNode* cur = plist;
while (cur)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
单链表的尾插也不是很难控制好着俩点就好了:
//单链表尾插
void SLTPushBack(SLTNode** pplist, SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
if (*pplist == NULL)
{
*pplist = newnode;
}
else
{
SLTNode* tail = *pplist;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
这个可以说是最简单的部分了,只要让要 plist 指向 插入的节点,插入节点的 next 指向下一个节点。
//单链表头插
void SLTPushFront(SLTNode** pplist,SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
newnode->next = *pplist;
*pplist = newnode;
}
单链表的头插尾插我们实现了下面就是单链表的尾删了。注意要控制好边界的几种情况就好了
//单链表尾删
void SLTPopBack(SLTNode** pplist)
{
//非空判断
assert(*pplist);
SLTNode* tail = *pplist;
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
单链表的头删注意考虑俩总情况,一个是只有一个节点释放,一个是多个节点释放。
//单链表头删
void SLTPopFront(SLTNode** pplist)
{
//非空判断
assert(*pplist);
SLTNode* newhead = (*pplist)->next;
free(*pplist);
*pplist = newhead;
}
为什么要先写单链表的查找呢,因为我们现实中通常不知道我们要删除或者插入的数在第一个点上所以需要先查找要删除或者插入的数到时候删除直接复用就好了。
//查找节点
SLTNode* SLTFind(SLTNode* plist, SLTDataType x)
{
SLTNode* cur = plist;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
和前面的删除一样,需要控制好不同的情况和 暴力检测一下 plist 传过来的是不是一个空指针。
//在pos以前插入x
void SLTInsert(SLTNode** pplist, SLTNode* pos, SLTDataType x)
{
assert(*pplist);
assert(pos);
if (pos == *pplist)
{
SLTPushFront(pplist,x);
}
else
{
SLTNode* prev = *pplist;
while (prev->next != pos)
{
prev = prev->next;
}
SLTNode* newnode = BuySListNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
这个就没在pos之前插入x那么复杂了可以根据指针找到下一个节点,然后删除pos的后一个节点
// 在pos以后插入x
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
if (pos->next == NULL)
{
pos->next = newnode;
}
else
{
SLTNode* next = pos->next->next;
pos->next = newnode;
newnode->next = next;
}
}
删除pos的位置就需要循环遍历pos,的前一个位置。然后进行 free 释放空间
// 删除pos位置
void SLTErase(SLTNode** pplist, SLTNode* pos)
{
assert(pplist);
assert(pos);
if(*pplist == pos)
{
SLTPopFront(pplist);
}
else
{
SLTNode* prve = *pplist;
while (prve->next != NULL)
{
prve = prve->next;
}
prve->next = pos->next;
free(pos);
}
}
// 删除pos的后一个位置
void SLTEraseAfter(SLTNode* pos)
{
assert(pos);
assert(pos->next);
SLTNode* posNext = pos->next;
pos->next = posNext->next;
free(posNext);
posNext = NULL;
}
这里博主就不给大家测试给大家写个样例,大家自己去试试增删查改哦!
void Test_SList2()
{
SLTNode* plist = NULL;
SLTPushFront(&plist, 1);
SLTPushFront(&plist, 2);
SLTPushFront(&plist, 3);
SLTPushFront(&plist, 4);
SLTPushFront(&plist, 5);
SLTPrint(plist);
}
int main()
{
Test_SLsit1();
}
✅ 归纳:
好了以上就是关于分支语句 链表 的所有知识点了,大家快下去练习练习吧!
链表的介绍
链表的结构
链表的增删查改
☁️ 把本章的内容全部掌握,铁汁们就可以熟练应用switch语句啦!
看到这里了还不给博主扣个:
⛳️ 点赞
☀️收藏
⭐️ 关注
!
❤️
拜托拜托这个真的很重要!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。