void SLTPopBack(SLTNode** pphead)
大家好,我是weipyh,一个C++的初学者
上期我们讲了顺序表的内容,这期接下来讲单链表
3.单链表
3.1概念与结构
概念:链表是⼀种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的
逻辑结构一定是线性的;物理结构大多数情况下不是线性的
3.11结点(节点)
链表中的每个结点都是独立申请空间,每个结点储存两部分信息:
-
数据域(Data):用于存储实际的数据。
-
指针域(Pointer):用于存储指向下一个节点的指针(或地址)。
3.12链表的性质
// 定义单链表节点数据类型
typedef int SLTDataType;
// 定义单链表节点结构体
typedef struct SListNode
{
SLTDataType data; // 数据域,用于存储节点的数据
struct SListNode* next; // 指针域,用于指向下一个节点
} SLTNode;
3.13链表的打印
初始化链表
void test01()
{
// 创建四个节点
SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode)); // 创建第一个节点
SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode)); // 创建第二个节点
SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode)); // 创建第三个节点
SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode)); // 创建第四个节点
// 设置节点的数据
node1->data = 1; // 第一个节点数据为1
node2->data = 2; // 第二个节点数据为2
node3->data = 3; // 第三个节点数据为3
node4->data = 4; // 第四个节点数据为4
// 链接节点,形成单链表
node1->next = node2; // 第一个节点指向第二个节点
node2->next = node3; // 第二个节点指向第三个节点
node3->next = node4; // 第三个节点指向第四个节点
node4->next = nullptr; // 第四个节点的指针域为空,表示链表结束
// 使用指针 plist 指向链表的头节点
SLTNode* plist = node1; // plist 是链表的头指针,指向第一个节点
// 调用 SLTPrint 函数打印链表
SLTPrint(plist); // 假设 SLTPrint 是一个用于打印链表的函数
}
SLTNode* pcur = phead; // 定义一个指针 pcur,初始化为链表的头节点 phead
while (pcur) // 遍历链表,直到 pcur 为 nullptr(链表结束)
{
printf("%d -> ", pcur->data); // 打印当前节点的数据
pcur = pcur->next; // 将指针移动到下一个节点
}
printf("nullptr\n"); // 打印链表结束标志
3.2单链表的实现
SLTNode* SLTBuyNode(SLTDataType); //创建并初始化新结点
// 创建一个新的单链表节点,并初始化其数据
SLTNode* SLTBuyNode(SLTDataType x)
{
// 分配内存空间给新节点
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
// 检查内存分配是否成功
if (newnode == nullptr)
{
// 如果内存分配失败,打印错误信息并退出程序
perror("malloc fail");
exit(1);
}
// 初始化新节点的数据域
newnode->data = x;
// 初始化新节点的指针域为 nullptr,表示当前没有后续节点
newnode->next = nullptr;
// 返回新创建的节点
return newnode;
}
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
// 创建一个新节点并初始化
SLTNode* newnode = SLTBuyNode(x);
// 如果链表为空,直接将新节点设置为头节点
if (*pphead == nullptr)
{
*pphead = newnode;
}
// 如果链表不为空,找到尾节点并将新节点链接到尾部
else
{
SLTNode* ptail = *pphead; // 从头节点开始
while (ptail->next) // 找到尾节点
{
ptail = ptail->next;
}
ptail->next = newnode; // 将新节点链接到尾部
}
}
void SLTPushFront(SLTNode** pphead, SLTDataType x);//头插
#include <assert.h> // 引入 assert 宏
// 在单链表头部插入一个新节点
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
// 断言检查:确保传入的二级指针非空
assert(pphead);
// 创建并初始化新节点
SLTNode* newnode = SLTBuyNode(x);
// 将新节点的 next 指针指向当前的头节点
newnode->next = *pphead;
// 更新头指针,使其指向新节点
*pphead = newnode;
}
void SLTPopBack(SLTNode** pphead) //尾删
void SLTPopBack(SLTNode** pphead)
{
// 断言检查:确保传入的二级指针非空且链表不为空
assert(pphead && *pphead);
// 如果链表只有一个节点,直接释放该节点并清空头指针
if ((*pphead)->next == nullptr)
{
free(*pphead); // 释放头节点
*pphead = nullptr; // 清空头指针
}
else
{
// 如果链表有多个节点,找到倒数第二个节点和最后一个节点
SLTNode* prev = nullptr; // 初始化前驱节点指针为nullptr
SLTNode* ptail = *pphead; // 初始化尾节点指针为头节点
while (ptail->next) // 遍历链表直到最后一个节点
{
prev = ptail; // 更新前驱节点
ptail = ptail->next; // 移动尾节点指针到下一个节点
}
// 删除最后一个节点
prev->next = nullptr; // 将前驱节点的next指针设为nullptr,断开与尾节点的连接
free(ptail); // 释放最后一个节点的内存
ptail = nullptr; // 防止悬空指针,将尾节点指针设为nullptr
}
void SLTPopFront(SLTNode** pphead); //头删
void SLTPopFront(SLTNode** pphead)
{
// 断言检查:确保传入的二级指针非空且链表不为空
assert(pphead && *pphead);
// 保存下一个节点的地址,以便后续操作
SLTNode* next = (*pphead)->next;
// 释放当前头节点的内存
free(*pphead);
// 更新头指针,使其指向下一个节点
*pphead = next;
}
SLTNode* SLTFindNode(SLTNode* phead, SLTDataType x) //查找
// 在链表中查找值为 x 的节点
SLTNode* SLTFindNode(SLTNode* phead, SLTDataType x)
{
SLTNode* pcur = phead; // 初始化当前节点指针为头节点
while (pcur) // 遍历链表直到末尾(nullptr)
{
if (pcur->data == x) // 如果当前节点的数据等于 x
{
return pcur; // 返回当前节点
}
pcur = pcur->next; // 移动到下一个节点
}
return nullptr; // 如果没有找到,返回 nullptr
}
void SLTInsert(SLTNode** pphead, SLTNode * pos, SLTDataType x); //指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
// 断言检查:确保传入的二级指针非空且链表不为空,且 pos 节点在链表中
assert(pphead && *pphead);
assert(pos);
// 如果 pos 是头节点,直接在头部插入新节点
if (pos == *pphead)
{
SLTPushFront(pphead, x);
}
else
{
// 创建并初始化新节点
SLTNode* newnode = SLTBuyNode(x);
// 找到 pos 节点的前驱节点
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
// 确保 prev 不会越界,即 pos 节点确实在链表中
assert(prev != nullptr);
}
// 将新节点插入到 pos 节点之前
newnode->next = pos;
prev->next = newnode;
}
}
void SLTErase(SLTNode** pphead, SLTNode* pos); // 指定位置删除结点
// 在链表中删除指定节点 pos
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
// 断言检查:确保 pos 节点和传入的二级指针非空,且链表不为空
assert(pos && pphead && *pphead);
SLTNode* prev = *pphead; // 初始化前驱节点指针为头节点
// 遍历链表找到 pos 节点的前驱节点
while (prev->next != pos)
{
prev = prev->next;
// 确保 prev 不会越界,即 pos 节点确实在链表中
assert(prev != nullptr);
}
// 删除 pos 节点
prev->next = pos->next; // 将前驱节点的 next 指针指向 pos 节点的下一个节点
free(pos); // 释放 pos 节点的内存
pos = nullptr; // 防止悬空指针,将 pos 指针设为 nullptr
}
void SLTInsertAfter(SLTNode* pos, SLTDataType x); //指定位置之后插入数据
// 在链表中指定节点 pos 之后插入一个新节点
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
// 断言检查:确保 pos 节点非空
assert(pos);
// 创建并初始化新节点
SLTNode* newnode = SLTBuyNode(x);
// 将新节点插入到 pos 节点之后
newnode->next = pos->next; // 新节点的 next 指针指向 pos 节点的下一个节点
pos->next = newnode; // 将 pos 节点的 next 指针指向新节点
}
void SLTEraseAfter(SLTNode* pos); //删除pos之后的结点
// 在链表中删除指定节点 pos 之后的节点
void SLTEraseAfter(SLTNode* pos)
{
// 断言检查:确保 pos 节点非空且 pos 之后有节点
assert(pos && pos->next);
// 保存要删除的节点
SLTNode* del = pos->next;
// 将 pos 节点的 next 指针指向 del 节点的下一个节点,从而跳过 del 节点
pos->next = pos->next->next; // (pos.next = del.next)
// 释放要删除的节点的内存
free(del);
// 防止悬空指针,将 del 指针设为 nullptr
del = nul
void SListDestroy(SLTNode** pphead); //销毁链表
// 销毁整个单链表,释放所有节点的内存
void SListDestroy(SLTNode** pphead)
{
// 断言检查:确保传入的二级指针非空
assert(pphead);
// 初始化当前节点指针为头节点
SLTNode* pcur = *pphead;
// 遍历链表直到末尾(nullptr)
while (pcur != nullptr)
{
// 保存下一个节点的地址,以便后续操作
SLTNode* next = pcur->next;
// 释放当前节点的内存
free(pcur);
// 移动到下一个节点
pcur = next;
}
// 将头指针设为 nullptr,表示链表已被销毁
*pphead = nullptr;
}
这篇的内容就到这里了,欢迎大家讨论,感谢观看。
&spm=1001.2101.3001.5002&articleId=146043282&d=1&t=3&u=ad5c02b4e8dc450ca3a363f700077f26)
2481

被折叠的 条评论
为什么被折叠?



