☃️个人主页:fighting小泽
作者简介:目前正在学习C语言和数据结构
博客专栏:数据结构
️欢迎关注:评论点赞留言
在前面我们已经学习过了有关顺序表的知识,但是我们知道顺序表是存在着一些问题的
问题:
所以我们能不能寻求其他的解决方案?
顺序表的一切根源就是一块连续的空间,那我们能不能用不连续的空间存储数据呢?
这个时候就要用到我们的链表了。
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:
单链表中,每一个节点存储一个数据和指向下一个节点的指针,最后一个节点指向NULL。
优点:
缺点:
#include
#include
#include
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
void SLTprint(SLTNode* phead);//打印
void SLTPushFront(SLTNode** pphead, SLTDataType x);//前插
SLTNode* BuyTNode(SLTDataType x);//malloc一个新节点
void SLTPushBack(SLTNode** pphead, SLTDataType x);//尾插
void SLTPopBack(SLTNode** pphead);//尾删
void SLTPopFront(SLTNode** pphead);//头删
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);//查找某个节点
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);//在pos指针处插入元素
void SLTInsertAfter(SLTNode* pos, SLTDataType x);//在pos指针后插入元素
void SLTErase(SLTNode** pphead, SLTNode* pos);//删除pos指针指向的节点
void SLTEraseAfter(SLTNode* pos);//删除pos指针后一个节点
void SLTDestroy(SLTNode* phead);//删除列表,释放maollc的节点
遍历一遍链表打印即可
void SLTprint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
SLTNode* BuyTNode(SLTDataType x)
{
SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
if (node == NULL)
{
perror("malloc");
return 0;
}
node->data = x;
node->next = NULL;
return node;
}
迭代释放链表节点
void SLDestroy(SLTNode** pphead)
{
assert(pphead);
SLTNode* cur = *pphead;
while (cur)
{
SLTNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
单链表的优势就是头插头删
对于头删,我们要断言二级指针指向的一级指针是否为空。若为空说明该链表没有元素,assert会报错。
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuyTNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
void SLTPopFront(SLTNode** pphead)
{
assert(*pphead);
SLTNode* tail = *pphead;
*pphead = tail->next;
free(tail);
}
尾插时,需要分类讨论链表是否为空的情况
尾删时,同样需要链表是否为空。同样需要分开讨论仅剩一个头节点或剩余多个节点的情况。若只剩一个节点,删除后将头指针置 NULL 。若剩余多个节点,则删除最后一个节点后将尾节点的 next 置 NULL 。
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuyTNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
return;
}
else
{
SLTNode* tail = *pphead;
while ((tail->next) != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
void SLTPopBack(SLTNode** pphead)
{
assert(*pphead);
SLTNode* tail = *pphead;
if (tail->next == NULL)
{
free(*pphead);
*pphead = NULL;
return;
}
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
遍历一遍链表,查找到返回结构体的指针,反之返回NULL
因为已经找到了结构体的指针,所以可以直接修改结构体的变量
查找函数是要配合下面的pos节点插入、删除函数使用的
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
注意要分类讨论 pos 是否为头节点
此接口不常用,因为单链表的前一个节点不好找,算法效率低
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pos);
if (*pphead == pos)
{
SLTPushFront(pphead, x);
return;
}
SLTNode* cur = *pphead;
while (cur)
{
if (cur->next == pos)
{
SLTNode* newnode = BuyTNode(x);
newnode->next = cur->next;
cur->next = newnode;
return;
}
else
cur = cur->next;
}
}
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pos);
SLTNode* cur = *pphead;
if (*pphead == pos)
{
SLTPopFront(pphead);
//*pphead = cur->next;
//free(cur);
return;
}
while (cur)
{
if (cur->next == pos)
{
cur->next = pos->next;
free(pos);
return;
}
else
cur = cur->next;
}
}
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode = BuyTNode( x);
newnode->next = pos->next;
pos->next = newnode;
}
void SLTEraseAfter(SLTNode* pos)
{
assert(pos);
SLTNode* cur = pos->next;
pos->next = cur->next;
free(cur);
}
这些就是我给大家分享的关于链表的知识啦,希望我们都能有所收获!
先赞后看,养成习惯!!^ _ ^
码字不易,大家的支持就是我坚持下去的动力,点赞后不要忘了关注我哦!
如有错误,还请您批评改正(。ì _ í。)