前言
本文是用代码的形式对数据结构单链表进行实现并进行增删查改等操作,本文所有的代码都是利用 C 语言来编写的。
本文全部代码与板书集合:单链表
注:上面的全部代码分为三个模块:头文件(List.h)、函数具体实现文件(List.c)、测试文件(Test.c)。
链表
1.1 链表的概念及结构
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的 。
结构形态:
注:
- 从上图可以看出,链式结构在逻辑上是连续存放的,但是在物理空间上不一定连续存放。
- 现实中节点向内存申请空间是在堆上申请的。
单项链表的实现
节点的结构:
//为了方便管理,改变数据类型。
typedef int SLNDataType;
typedef struct SListNode
{
SLNDataType val;//存的值
struct SListNode* next;//下一个节点
}SLNode;//别名
一、创建链表节点
解析:我们利用 malloc 函数动态开辟一块空间,为了防止其因为空间开辟失败而出现的程序问题,减少排错时间,我们利用 perror 进行提示。然后把其开辟的节点的 val = x ,因为不知道其节点指向的空间先置于 NULL。
SLNode* CreateNode(SLNDataType x)
{
SLNode* newNode = (SLNode*)malloc(sizeof(SLNode));
if (newNode == NULL)//防止 malloc 开辟空间失败
{
perror("malloc fail!!!");
}
newNode->val = x;
newNode->next = NULL;
return newNode;
}
二、打印链表
解析:为了防止 phead 的改变,我们使用 cur 来代替 phead 进行打印。当 cur 为NULL循环停止。
void SLTPrint(SLNode* phead)
{
SLNode* cur = phead;
while (cur != NULL)
{
printf("%d ----》 ", cur->val);
cur = cur->next;
}
printf("NULL\n");
}
三、单链表的尾部插入
解析:
void SLTPushBack(SLNode** pphead, SLNDataType x)
{
SLNode* newNode = CreateNode(x);
if (pphead == NULL)
{
*pphead = newNode;
}
else
{
SLNode* tail = pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newNode;
}
}
注:结构体指针才能访问结构体,当需要去改变结构体指针的值的时候,我们根据前面所学的知识得知:函数的形参只是实参的临时拷贝,在函数里面改变形参并不能影响实参,我们需要去传地址通过地址去访问才能改变实参。所以,当我们想要改变结构体指针的时候我们需要传递结构体指针的地址,一级指针需要二级指针存放。
四、单链表的头部插入
解析:
void SLTPushFront(SLNode** pphead, SLNDataType x)
{
SLNode* newhead = CreateNode(x);
newhead->next = *pphead;
*pphead = newhead;
}
五、单链表的尾部删除
解析:
void SLTPopBack(SLNode** pphead)
{
SLNode* tail = *pphead;
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
六、单链表的头部删除
解析:
void SLTPopFront(SLNode** pphead)
{
SLNode* first = *pphead;
*pphead = (*pphead)->next;
free(first);
}
七、单链表的查找
解析:
SLNode* SLTFind(SLNode* phead, SLNDataType x)
{
SLNode* cur = phead;
while (cur != NULL)
{
if (cur->val == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
八、单链表的插入
解析:
//在pos前面插入
SLNode* SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x)
{
if (*pphead == pos)
{
//头插
SLTPushFront(pphead, x);
}
else
{
SLNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
SLNode* newnode = CreateNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
九、单链表节点的删除
解析:
void SLTErase(SLNode** pphead, SLNode* pos)
{
if (*pphead == pos)
{
// 头删
SLTPopFront(pphead);
}
else
{
SLNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
十、单链表销毁
void SLTDestroy(SLNode** pphead)
{
SLNode* cur = *pphead;
while (cur)
{
SLNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
后语
希望各位在翻阅过本篇文章各位能够对 链表 的理解有更加深刻。
希望能对各位有所帮助,如果各位有任何疑问,欢迎各位留言,我们可以进行友好的探讨与交流。
欢乐的时间总是过得特别快。又到时间讲bye,我们下一篇再见!!!