系列文章目录
🎈 🎈 我的CSDN主页:OTWOL的主页,欢迎!!!👋🏼👋🏼
🎉🎉我的C语言初阶合集:C语言初阶合集,希望能帮到你!!!😍 😍
🔍🔍我的C语言进阶合集:我的C语言进阶合集,期待你的点击!!!🌈🌈
🎉🎉我的数据结构与算法合集:数据结构与算法合集,点进去看看吧!!! 🎊🎊
😍 👋🏼🎉🎊创作不易,欢迎大家留言、点赞加收藏!!! 🥳😁😍
文章目录
- 系列文章目录
- 前言
- 一、链表
- (1)定义
- (2)分类
- 二、单链表(无头结点单向非循环链表)
- (1)定义
- (2)物理结构
- 三、单链表的增删改查等方法的实现
- (1)打印单链表
- (2)动态申请新结点
- (3)尾插单链表
- (4)头插单链表
- (5)尾删单链表
- (6)头删单链表
- (7)单链表查找
- (8)在 pos 位置 之前插入新的结点
- (9)删除在 pos 位置的结点
- (10)在 pos 位置 之后插入新的结点
- (11)删除 pos 位置之后的结点
- (12)单链表的销毁
- END
前言
各位博友,大家好!👋 今天为大家分享 单链表 的总结🔍。
一、链表
(1)定义
链表是一种物理存储结构上非连续、非顺序的存储结构,
数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
(2)分类
链表的结构非常多样,以下情况组合起来就有8种(2 x 2 x 2)链表结构:
补充说明:
带头链表里的头结点,实际为“哨兵位”,哨兵位结点不存储任何有效元素,只是站在这里“放哨的”
二、单链表(无头结点单向非循环链表)
(1)定义
无头结点单向非循环链表:结构简单,一般不会单独用来存数据。
实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。
另外这种结构在笔试面试中出现很多。
(2)物理结构
补充说明:
结点的组成主要有两个部分:当前结点要保存的数据(data
)和保存下⼀个结点的地址(指针变量next
)。
三、单链表的增删改查等方法的实现
(1)打印单链表
// 定义函数 SLTPrint,用于打印单链表
void SLTPrint(SLTNode* phead)
{
// 定义一个指针 cur,用于遍历链表,初始指向链表头结点
SLTNode* cur = phead;
// 使用 while 循环遍历链表,直到 cur 指向 NULL,即链表结束
while (cur)
{
// 打印当前结点的数据域
printf("%d->", cur->data);
// 将 cur 指针移动到下一个结点
cur = cur->next;
}
// 当遍历结束,打印"NULL"表示链表结束
printf("NULL\n");
}
(2)动态申请新结点
// 定义函数 BuySLTNode,用于动态申请一个单链表结点,并初始化其数据
SLTNode* BuySLTNode(SLTDataType x)
{
// 初始化 newnode 为 NULL,用于存放新申请的结点
SLTNode* newnode = NULL;
// 动态申请一个 SLTNode 大小的内存空间,并将其首地址以 SLTNode 指针形式返回
SLTNode* tmp = (SLTNode*)malloc(sizeof(SLTNode));
// 如果内存申请失败,打印错误信息并返回 NULL
if (tmp == NULL)
{
perror("malloc fail");
return NULL;
}
// 将 tmp 赋值给 newnode,表示 newnode 指向新申请的结点
newnode = tmp;
// 初始化新结点的数据域为传入的参数 x
newnode->data = x;
// 初始化新结点的 next 指针为 NULL,表示该结点目前没有后继结点
newnode->next = NULL;
// 返回新申请并初始化的结点
return newnode;
}
(3)尾插单链表
// 定义函数 SLTPushBack,用于在单链表尾部插入一个新结点
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
// 断言 pphead 不为空,确保指向单链表首结点指针的指针 pphead 有效
assert(pphead);
// 动态申请一个新结点,并初始化其数据为 x
SLTNode* newnode = BuySLTNode(x);
// 如果链表为空(首结点为空),则将新节点设置为首结点
if (*pphead == NULL)
{
*pphead = newnode;
}
// 如果链表不为空
else
{
// 定义一个指针 tail,用于遍历链表找到尾结点
SLTNode* tail = *pphead;
// 遍历链表直到 tail 指向尾结点(tail->next 为 NULL)
while (tail->next)
{
tail = tail->next;
}
// 将尾结点的 next 指针指向新结点,完成尾部插入操作
tail->next = newnode;
}
}
(4)头插单链表
// 定义函数 SLTPushFront,用于在单链表头部插入一个新结点
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
// 断言 pphead 不为空,确保指向单链表首结点指针的指针 pphead 有效
assert(pphead);
// 动态申请一个新结点,并初始化其数据为 x
SLTNode* newnode = BuySLTNode(x);
// 将新结点的 next 指针指向当前的首结点,这样新节点就成为了新的首结点
newnode->next = *pphead;
// 更新头指针,使其指向新结点
*pphead = newnode;
}
(5)尾删单链表
// 定义函数 SLTPopBack,用于删除单链表尾部的结点
void SLTPopBack(SLTNode** pphead)
{
// 断言 pphead 不为空,确保指向单链表首结点指针的指针 pphead 有效
assert(pphead);
// 断言链表不为空,确保至少有一个结点
assert(*pphead);
// 如果链表只有一个结点,直接释放该结点并设置首指针为 NULL
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
// 如果链表有多个结点
else
{
// 定义两个指针,tail 指向当前尾结点,prev 指向尾节点的前一个结点
SLTNode* tail = *pphead;
SLTNode* prev = NULL;
// 遍历链表直到 tail 指向尾结点(tail->next 为 NULL)
while (tail->next)
{
prev = tail;
tail = tail->next;
}
// 将 prev 的 next指针设置为 NULL,从链表中移除尾结点
prev->next = NULL;
// 释放尾结点的内存
free(tail);
// 将 tail 设置为 NULL,避免悬挂指针
tail = NULL;
}
}
(6)头删单链表
// 定义函数 SLTPopFront,用于删除单链表的首结点
void SLTPopFront(SLTNode** pphead)
{
// 断言 pphead 不为空,确保指向单链表首结点指针的指针 pphead 有效
assert(pphead);
// 断言链表不为空,确保至少有一个结点
assert(*pphead);
// 保存当前首结点的指针,用于后续释放内存
SLTNode* first = *pphead;
// 将头指针更新为首结点的下一个结点,即新的首结点
*pphead = first->next;
// 释放原首结点的内存
free(first);
// 将 first 指针设置为 NULL,避免悬挂指针
first = NULL;
}
(7)单链表查找
// 定义函数 SListFind,用于在单链表中查找一个特定值的结点
SLTNode* SListFind(SLTNode* plist, SLTDataType x)
{
// 初始化指针 cur 指向链表的首结点 plist
SLTNode* cur = plist;
// 使用 while 循环遍历链表,直到 cur 指向 NULL,即链表结束
while (cur)
{
// 如果当前结点的数据域 data 等于要查找的值 x
if (cur->data == x)
{
// 返回当前结点的指针,表示找到了匹配的结点
return cur;
}
// 将 cur 指针移动到下一个结点
cur = cur->next;
}
// 如果遍历完整个链表都没有找到匹配的结点,返回 NULL
return NULL;
}
(8)在 pos 位置 之前插入新的结点
// 定义函数 SLTInsert,用于在单链表的 pos 位置之前插入一个新结点,该新结点的数据为 x
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
// 断言 pphead 不为空,确保指向单链表首结点指针的指针 pphead 有效
assert(pphead);
// 断言 pos 不为空,确保传入的位置结点有效
assert(pos);
// 动态申请一个新节点,并初始化其数据为 x
SLTNode* newnode = BuySLTNode(x);
// 如果 pos 是首结点,即在头部插入新结点
if (pos == *pphead)
{
SLTPushFront(pphead, x); // 调用头插函数插入新结点
}
// 如果 pos 不是首结点也不是空结点
else
{
// 定义一个指针 prev,用于遍历链表找到 pos 的前一个结点
SLTNode* prev = *pphead;
// 遍历链表直到 prev 的 next 指向 pos
while (prev->next != pos)
{
prev = prev->next;
}
// 将 prev 的 next 指向新结点,并将新结点的 next 指向 pos,完成插入操作
prev->next = newnode;
newnode->next = pos;
}
}
(9)删除在 pos 位置的结点
// 定义函数 SLTErase,用于删除单链表中位于 pos 位置的结点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
// 断言 pphead 不为空,确保指向单链表首结点指针的指针 pphead 有效
assert(pphead);
// 断言 pos 不为空,确保传入的位置结点有效
assert(pos);
// 如果 pos 是首结点,调用头删函数删除首结点
if (pos == *pphead)
{
SLTPopFront(pphead);
}
// 如果 pos 不是首结点也不是空结点
else
{
// 定义一个指针 prev,用于遍历链表找到 pos 的前一个结点
SLTNode* prev = *pphead;
// 遍历链表直到 prev 的 next 指向pos
while (prev->next != pos)
{
prev = prev->next;
}
// 将 prev 的 next 指向 pos 的下一个节点,从而从链表中移除 pos 结点
prev->next = pos->next;
// 释放 pos 结点的内存
free(pos);
}
}
(10)在 pos 位置 之后插入新的结点
// 定义函数 SListInsertAfter,用于在单链表中 pos 位置的结点之后插入一个新的结点,新结点的数据为 x
void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
// 动态申请一个新的结点,并初始化其数据为 x
SLTNode* newnode = BuySLTNode(x);
// 将新结点的 next 指针指向 pos 节点的下一个节点
newnode->next = pos->next;
// 将 pos 结点的 next 指针更新为新结点,完成在 pos 之后插入新结点的操作
pos->next = newnode;
}
(11)删除 pos 位置之后的结点
// 定义函数 SListEraseAfter,用于删除单链表中 pos 位置结点之后的结点
void SListEraseAfter(SLTNode* pos)
{
// 断言 pos 不为空,确保传入的位置结点有效
assert(pos);
// 断言 pos 的下一个结点不为空,确保有结点可以删除
assert(pos->next);
// 保存 pos 结点的下一个结点的指针,这个结点将被删除
SLTNode* del = pos->next;
// 将 pos 结点的 next 指针更新为 del 结点的下一个结点,从而跳过 del 结点
pos->next = del->next;
// 释放 del 结点的内存
free(del);
// 将 del 指针设置为 NULL,避免悬挂指针
del = NULL;
}
(12)单链表的销毁
// 定义函数 SLTDestroy,用于销毁单链表,释放所有结点的内存
void SLTDestroy(SLTNode** pphead)
{
// 断言 pphead 不为空,确保指向单链表首结点指针的指针 pphead 有效
assert(pphead);
// 初始化 cur 指针指向链表的头结点
SLTNode* cur = *pphead;
// 初始化 next 指针,用于临时存储 cur 结点的下一个结点
SLTNode* next = NULL;
// 使用 while 循环遍历链表,直到 cur 指向 NULL,即链表结束
while (cur)
{
// 保存当前节点的下一个结点到 next
next = cur->next;
// 释放当前结点的内存
free(cur);
// 将 cur 指针更新为 next,继续遍历
cur = next;
}
// 遍历结束后,将首指针设置为 NULL,表示链表已被销毁
*pphead = NULL;
}
END
每天都在学习的路上!
On The Way Of Learning