目录
一.✨链表的概念及结构
🎈概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
现实中,数据结构中
注意:
1.从上图可以看出,链式结构在逻辑上是连续,的但是在物理上不一定连续 。
2.现实中的节点一般都是从堆上申请出来的
3.从堆上申请的空间,是按照一定策略来分配的,俩次申请的空间可能连续,也可能不连续
二.✨链表的分类
🎈实际中链表的结构非常多样
1.🎈 带头的或者不带头的
2.🎈循环的或者不循环的
3,🎈单向的或者双向的 等等等等
而今天我们主要讲的就是单链表和带头循环链表
1. 🎈无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
2. 🎈带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了
三.✨单链表的实现
🎈单链表的结构,一个单元存储数据,一个单元存储下一个数据的地址;
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode;
1.🎈动态申请一个节点
单链表的插入,首先要先创建一个节点,我们用malloc来创建一个数据
SListNode* BuySListNode(SLTDateType x)
{
SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
2.🎈单链表的尾插
单链表的尾部插入,首先动态申请一个新节点,然后判断链表,是否为空,为空直接赋值给链表,如果不为空,找到链表的尾部 然后进行插入。
void SListPushBack(SListNode** pplist, SLTDateType x)
{
SListNode* newnode = BuySListNode(x);
if (*pplist == NULL)
{
*pplist = newnode;
}
else
{
SListNode* tail = *pplist;
// 找尾
while (tail->next)
{
tail = tail->next;
}
tail->next = newnode;
}
}
3.🎈单链表的尾删
void SListPopBack(SListNode** pplist)
{
//断言 链表为空
assert(*pplist);
//链表就一个元素
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else //链表为多元素
{
SListNode* tail = *pplist;
while (tail->next->next)//找到倒数第二个元素
{
tail = tail->next;//最后tail 指向的是倒数第二个元素
}
free(tail->next);//释放掉最后一个
tail->next = NULL;//倒数第二个元素变为最后一个 置为空
}
}
4.🎈单链表的头插
看到这个代码是不是很惊喜 这就是单链表的头插 ,也是单链表的优势所在,相较于顺序表的头插,单链表的头插十分的方便,申请节点之后 把节点指向头节点,然后再把头节点变为新申请的节点。
void SListPushFront(SListNode** pplist, SLTDateType x)
{
SListNode* newnode = BuySListNode(x);
newnode->next = *pplist; //new 链接 pp
*pplist = newnode;
}
5.🎈单链表的头删
头删相较于头插一样,也是十分的方便的,先保存头部指向的下一处地址,然后删除头部,把头部置成头部下一处地址处就好了。
void SListPopFront(SListNode** pplist)
{
SListNode* next = (*pplist)->next;//next 指向 头部的下一个地址
free(*pplist);//删除头部
*pplist = next;//头部指向下一个地址处·
}
6.🎈单链表的查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
SListNode* sur = plist;
while (sur)
{
if (sur->data == x)
{
return sur;
}
sur = sur->next;
}
return NULL;
}
7.🎈单链表的pos位置之后插入
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
assert(pos);
SListNode* cur = BuySListNode(x); //先动态申请一个节点
cur->next = pos->next;//先把后面的节点给新节点
pos->next = cur;//再把pos的下一个节点指向cur
}
8.🎈单链表的销毁
void SListDestroy(SListNode** plist)
{
SListNode* cur = *plist;
while (cur)
{
SListNode* newnode = cur->next;//新节点等于初始下一个
free(cur); //删除头
cur = newnode;// 头等于新节点
}
*plist = NULL;
}
四.✨总结
想一想为什么单链表的pos位置插入 为什么不在之前插入呢???
由上表查看,单链表的优势是头插头删,
但是单链表要是想像顺序表一样随机访问存储确实不行的
下一期 ,我们在一起探讨了解 带头双向循环链表,是不是听名字就很炫酷呢!