数据结构 —— 单链表的实现(附有源代码)
文章目录
每博一文案🔍🔍🔍
师傅说:很多感情的结束都并非突然,只是攒够了失望,然后离开的。
或许是争吵后平复下来的某一天,或许是散步路过曾经约会的公园,
也许最让人伤心的不是争吵。
而是亲眼见证曾经热情慢慢变淡,
从一开始的嘘寒问暖到后来的冷眼相待,从一开始的掏心掏肺到后来
的警醒硕放,曾经深爱过的那个人,最后却变成了这样,但伤心吗?或者
歇斯底里真到了临别之际,也许你只顺平静。
你,你,你,你,你,你真正的失望都是悄无声息的,
每次伤心一次就心上留下一道伤痕,起初每次都会很痛。
然而随着伤痕的积累,你的心慢慢变得麻木,变得寒冷麻木,
到即便离开,他也不会再疼无声得告别,都是因为绝望吧?
因为付出了太多,所以不会有后悔,
因为付出了太多,所以走得洒脱。
曾经为爱该做的都已经做了,就算最后一拍两散,那也是值得,
毕竟人生也没有几次愿意这般付出得无怨无悔,在每一段付出
真心的感情里,人人都是弱者。
每时忘一次,就会套上一层铠甲,到最后铠甲后,你变强了,而曾经相爱
过的两个人也再也无法拥抱了。
———————————— 一禅心里庙语
顺序表的问题:🔍🔍🔍
- 在顺序表中无论是中间/ 头部的插入,其时间的复杂度都是为 O(N)的
- 增容需要申请新的空间,拷贝数据,释放旧空间。会有不小的消耗,影响程序的执行速度。
- 在增容方面上一般是2倍或者是1.5倍,这样是为了,防止你在不断的开辟动态内存空间,大大消耗程序的效率
- 但是,这样做也同样存在一个巨大的弊端:浪费空间 ,例如:当前容量为 100 ,满了以后增容200 ,可我们需要插入的数据却只是需要 5 个而已,那么就会浪费 95 个数据空间。
单链表的概念🔍🔍🔍
- 就是基于顺序表的缺点,我们就有了:单链表的存在
- 概念:链表是一种物理存储结构上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针(地址)链接次序实现的。
- 如下图:单链表:逻辑结构示图: 😊😊😊
- 单链表:物理结构图 🔍🔍🔍
- 我们不难发现,我们的物理结构,表示的更加,清晰,简单,明了。😍😍😍
写项目的注意事项:🔍🔍🔍
- 使用文件分布的方式来写项目,这样项目内容分类明显,代码的可读性,可维护性,大大提高
- 每实现一个对应的功能就,运行,测试,看看是否存在错误,这样错误出现的范围就大大减小了,容易排查,修正,便于调试代码。
- 不要写了一大部分才运行测试,结果:出现一堆 BUG ,看得人都眼花缭乱,容易让你放弃
- 当然,如果出现一堆的 BUG ,也不要慌,一步一步的,监视,调试,改正代码,就可以了,总之就是不要,放弃😍😍😍
单链表的实现:💕💕💕💕💕💕💕💕💕💕💕💕💕💕💕
单链表的定义:🔍🔍🔍
- 这里我们使用 关键字 typedef 对单链表存放的数据,进行别名制 :方便后面的维护,修改,比如:修改数据类型为 :double
typedef int STLDataType;
- 创建自定义结构体:
- 这里存在一个注意事项:
- 如下:🔍🔍🔍
- 不可以结构体中嵌套了自身的结构体: 这里你想想看,我们如何计算该结构体的大小:**结构体中有结构体,再再结构体中:又有结构体,这样无限循环下去,这不就是无限循环了吗?其大小你能计算出来吗?,不能是吧?
struct SListNode
{
STLDataType data;
struct SListNode next; // 结构体中嵌套了结构体,
};
- 结果:🔍🔍🔍
- 所以:我们要使用指针,代表该类型的指针,其指针的大小:64位 8个字节,32 位 4个字节空间的大小
struct SListNode
{
STLDataType data; // 单链表的数值
struct SListNode* next; // 单链表存放下一个节点的指针
};
typedef struct SListNode SLTNode; // 该单链表结构体的别名
单链表的打印:🔍🔍🔍
- 通过结构体的引用,打印该结构体中的数值:
- 从下图(单链表物理示图),我们可以发现,当 指针(地址)=NULL ,后面就没有数值值了,所以我们打印的结束条件可以为 NULL
// 打印单链表的数值
void SListprint(SLTNode* phead)
{
SLTNode* cur = phead; // 这里我们使用 cur 变量来代替 phead头的访问
// 尽量不要移动头指针
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->data; // 移动指针
}
printf("NULL\n");
}
单链表的尾插法🔍🔍🔍
- 首先我们需要生成插入的节点;使用关键字 malloc
// 生成插入的节点
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
newnode->data = num;
newnode->next = NULL;
- 没有一个节点时的插入
- 存在多个节点的插入:
- 找到尾节点: 通过遍历 找到尾节点**(NULL)**
- 找到了尾节点,进行地址的交互,从而实现插入
tail = tail->next; // 移动指针
- 注意了 🔍🔍🔍 下面这段代码是有问题的
- 我们调用看看,再打印看看 💕💕💕
// 单链表的尾插法
void SListPushBack(SLTNode* phead, STLDataType num)
{
// 生成插入的节点
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
newnode->data = num;
newnode->next = NULL;
if (phead == NULL) // 没有节点时插入
{
phead = newnode;
}
else
{
SLTNode* tail = phead;
while (tail->next != NULL) // 或者 (tail->next)
{
tail = tail->next; // 移动指针;
}
// 当跳出循环,表示找到了,尾节点
tail = newnode->next;
}
}
int main()
{
SLTNode* plist = NULL;
SListprint(plist);
SListPushBack(plist, 99);
SListprint(plist);
return 0;
}
- 注意:结果:🔍🔍🔍
- 我们发现结果:并没有把99插入进单链表当中去.
- **其原因是:**🔍🔍🔍
注意:**形参和实参的关系:**🔍🔍🔍
我们的形参的改变并不会,影响到实参的改变
可以理解为,形参是实参的一份临时打拷贝,出了
对应的作用域,就会被释放,掉空间的
而我们这里传的是实参,接受的是形参,所以形参的赋值的改变
,不会影响这里的实参的改变,就出现了,现在的结果:尾插失败
==解决方法:==🔍🔍🔍`
- 首先,遇到问题不要慌,这个问题很好解决的:😊😊😊
我们不要传实参了,我们使用传地址 的方式就可以,就可以很好的解决这个问题了
注意: 因为我们传的是地址,所以要用指针,来接受这个地址,而请注意了一个关键点:
这里的地址是一级指针的地址 ,所以我们需要使用二级指针 接受这个一级指针的地址。
实现方法如下:😍😍😍
// 单链表的尾插法
void SListPushBack(SLTNode** pphead, STLDataType num)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
newnode->data = num;
newnode->next = NULL;
if (*pphead == NULL) // 注意是地址为 NULL,不是解引用
{
*pphead = newnode;
}
else
{
// 找到尾节点,从而实现尾插入
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next; // 移动指针
}
// 跳出循环,找到了,尾节点
tail->next = newnode;
}
}
- 测试结果:😊😍👍
单链表的头插法🔍🔍🔍
对创建节点的封装 🔍🔍🔍
- 这里我们为了方便多次,创建节点,我们把创建节点写成内部函数使用 static 关键字,进行封装函数,对于 关键字 static 还不了解的朋友可以移步到这 🔜🔜🔜:一点都不安静的 ——static_
- 注意: 这个函数的返回类型是:SLTNode* ,因为我们要返回的是一个单链表的节点的地址,地址吗!😊😊😊,自然需要,指针对吧,而且需要相对应的指针类型,这里是单链表的类型的指针
- 这里我们使用 操作符 sizeof() ,来计算该数据结构的大小,提高代码的兼容性,应用于不同位数的PC,想要详细了解的朋友可以移步到 🔜🔜🔜: 整形在内存中的存储的方式
- 实现如下:😍😍😍
// 创建节点
static SLTNode* BuySListNode(STLDataType num)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
newnode->data = num;
newnode->next = NULL;
return newnode;
}
- 头插法的物理图示: 😍😍😍
// 单链表的头插法
void SListPushFront(SLTNode** pphead, STLDataType num)
{
// 创建节点
SLTNode* newnode = BuySListNode(num);
newnode->next = *pphead;
*pphead = newnode;
}
newnode->next = *pphead;
*pphead = newnode;
注意:这段代码不可以交换的,
一旦交换了,你就会丢失整个单链表的
*pphead = newnode; newnode->next = *pphead;
看交换后,我们分析这段代码:
我们会发现,第一条语句,*pphead 这个原先指向整个单链表的地址,被 newnode 地址覆盖了,变成了newnode 的地址了,而我们原先的指向整个单链表的 的地址,没了,没了,意味着什么?—— 意味着,我们的单链表丢失了。
所以,千万注意,不要把链表丢失了
- 实现效果:
单链表的头删法🔍🔍🔍
- 注意:我们不可以一开始就把头节点释放掉了
- 一旦,一开始就释放空间了,我们的整个单链表就丢失了
- 所以我们需要先把头节点,临时保存起来,后,再释放
- 单链表的头删法的示图:
- 代码实现:
// 单链表的头删法
void SListPopFront(SLTNode** pphead)
{
SLTNode* next = (*pphead)->next; // 头节点的下一个节点的地址,
// 这里注意一点,就是*操作符和-> 结构体的引用的操作符的优先级一样,所以,带上括号
free(*pphead); // 释放空间
*pphead = next; // 重新指向,新的地址
}
- **结果:**💕💕💕
单链表的尾删法🔍🔍🔍
- 单链表的尾删法:存在三种情况:
- 当单链表为空节点的时候
- 直接返回,不用删除了,已经是空的了
- 当单链表为一个节点的时候
- 直接释放空间,置为空(NULL),
- 当单链表存在多个节点的时候
- 我们就要找到了两个节点的位置:一个是尾节点,另一个是倒数第二个节点的位置
- 我们需要把尾节点的空间释放掉,把倒数第二个节点的位置的next 置为 NULL ,因为后面没有数据了
- [x]
- **单链表的尾删法的图示:**💕💕💕
- **找到倒数第二个节点的位置的方法:**😍😍😍
- 我们可以使用一前一后两个指针 的方式:
SLTNode* prev = NULL; SLTNode* tail = *pphead; while (tail->next != NULL) { prev = tail; tail = tail->next; // 移动指针 } /*当tail->next不等于 NULL的时候, 我们把我们把tail的地址赋值给我们的后指针(prev) , 一直这样一前一后,直到 tail->next == NULL 我们就可以发现,尾节点的位置,和倒数第二个节点的位置我们也找到了*/
- **单链表的尾删法的代码实现:**😍😍😍
// 单链表的尾删法
void SListPopBack(SLTNode** pphead)
{
/*1.空节点
2.一个节点
3.多个节点*/
if (*pphead == NULL) // 空节点的情况
{
return;
}
else if ((*pphead)->next == NULL) // 一个节点的时候
{
free(*pphead);
*pphead = NULL;
}
else // 多个节点的时候
{
SLTNode* prev = NULL;
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
prev = tail;
tail = tail->next; // 移动指针
}
/*跳出循环,找到了尾节点,和倒数第二个节点的位置,开始操作交互*/
free(tail);
tail = NULL;
prev->next = NULL;
}
}
- **结果:**😊😊😊
单链表的查找🔍🔍🔍
-
直接遍历一遍单链表,看看是否有对应数值,有的话返回该数值的地址
-
**实现代码:**😊😊😊
// 单链表的查找
SLTNode* SListFind(SLTNode* phead, STLDataType num)
{
while (phead != NULL)
{
if (phead->data == num)
{
return phead; // 返回该数值的地址
}
phead = phead->next; // 移动指针
}
return NULL; // 不存在该数值,返回空;
}
单链表的中 pos (数值)位置前的插入数值🔍🔍🔍
- 首先我们需要遍历整个链表,查看是否有该数值,如果有,我们就进行插入,没有,是无法插入的
- **单链表的 pos (数值)位置前的插入数值的示图:**😊😊😊
- 同样关键点是找到,该数值的前一个数值的节点的位置,同样我们也是可以用,一前一后 的指针的
- 但是当插入的是头节点的时候,我们的前后指针,就没有它前一个节点了,这里我们就需要特殊处理了
- 特殊处理:当单链表为空的时候就不行了,我们可以复用 前面我们写过的头插法
- **代码实现如下:**😊😊😊
// 单链表 pow 位置的插入法
void SListInsert(SLTNode** pphead, STLDataType x, STLDataType num)
{
// 首先判断是否该数值是否存在
SLTNode* pos = (SListFind(*pphead, x));
if (pos) // 存在不为空(NULL),插入
{
if (pos == *pphead) // 插入位置,是头节点
{
SListPushFront(pphead, num); // 复用前面的头插法
}
else // 其他位置
{
SLTNode* newnode = BuySListNode(num); // 生成该插入数值的节点空间
SLTNode* prev = *pphead; // 代替头指针,遍历
while (prev->next != pos)
{
prev = prev->next; // 移动指针
}
prev->next = newnode;
newnode->next = pos;
}
}
}
- **其结果如下:**💕💕💕
单链表的中 pos(数值的)的删除🔍🔍🔍
- 同样我们在删除之前,也要判断一下该数值是否存在,存在才可以,删除的
- 原理是和单链表的 pos (数值)位置前的插入数值是一样的,
- 关键点: 就是找到该数值的 前节点的位置,从而才可以实现删除操作的
- 单链表的中 pos(数值的)的删除 的图示:如下😊😊😊
- 同样当删除的是头节点的时候,我们的前后指针,就没有它前一个节点了
- 但是我们同样可以,复用 ,前面我们实现过的头删法实现
- **具体代码实现如下:**💕💕💕
// 删除数值为 num 的节点
void SListErase(SLTNode** pphead, STLDataType num)
{
SLTNode* pos = SListFind(*pphead, num);
if (pos) // 判断数值是否存在
{
if (pos == *pphead) // 判断该数值是否是头节点
{
SListPopFront(pphead); // 复用头删法
}
else // 其他情况
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next; // 移动指针
}
// 找了,该数值的前一个节点的位置,删除操作
prev->next = pos->next;
free(pos); // 释放空间
pos = NULL; // 手动置为(空)NULL
}
}
}
- 其结果: 😊😊😊
完整的单链表 (文件分布式实现)🔍🔍🔍
-
先查看整体脉络:
-
SList.h 文件放的是各种需要的头文件,以及函数声明,自定义结构体,typedef 关键字的别名
-
Test.c 文件放的是 main 函数,各种测试操作
-
SLIst.c 文件放的是 关于 单链表 功能实现的有关的函数
-
-
😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊
SList.h 的代码:🔍🔍🔍
- SList.h 文件放的是各种需要的头文件,以及函数声明,自定义结构体,typedef 关键字的别名
- 😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊
#pragma once
#include<stdio.h>
#include<stdlib.h>
typedef int STLDataType; // 类型的的别名的使用,方便后面的维护,修改,比如但我们把数据改为 flost的类型的时候
struct SListNode // 创建单链表的结构体
{
STLDataType data; // 单链表的数值
struct SListNode* next; // 单链表的节点,注意:这里是指针地址的引用
};
typedef struct SListNode SLTNode; // 重新命名这个结构体的 别名。
///*注意下面这个是和上面那个的不同*/
//struct SListNode
//{
// SLDataType data;
// struct SListNode next;
// /*注意不可以:这样写的,这个代表的是:
// 结构体中套娃,嵌套了一个结构体,结构体中又嵌套了一个
// 结构体,这样无限嵌套结构体下去,就没有头了,你就不知道其大小了,sizeof()
// */
//};
// 注意:声明使用上 extern 关键字,代码好风格;
extern void SListPrint(SLTNode* phend); // 打印单链表
extern void SListPushBack(SLTNode** pphead, STLDataType num); // 单链表的尾插法
extern void SListPushFront(SLTNode** pphead, STLDataType num); // 单链表的头插法
extern void SListPopFront(SLTNode** pphead); // 单链表的头删法
extern void SListPopBack(SLTNode** pphead); // 单链表的尾删法;
extern void SListInsert(SLTNode** pphead, STLDataType x, STLDataType num); // 单链表中数值为x的节点后插入 num数值
extern void SListErase(SLTNode** pphead, STLDataType num); // 删除单链表中数值为 num 的节点
extern void SListErase_2(SLTNode** pphead, STLDataType num); // 删除单链表中数值为 num 的节点
extern void SListSeek(SLTNode* phead, STLDataType num); // 查找单链表中是否存在该数值的节点
😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊
Test.h 的代码:🔍🔍🔍
- 文件放的是 main 函数,各种测试操作
- 😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊
#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"
void test1()
{
SLTNode* plist = NULL; // 创建头节点
SListPushBack(&plist, 0); // 单链表的尾插法
SListPushBack(&plist, 1); // 单链表的尾插法
SListPushBack(&plist, 2); // 单链表的尾插法
SListPrint(plist); // 单链表的打印
SListPushFront(&plist, 10); // 单链表单位头插法
SListPushFront(&plist, 100); // 单链表单位头插法
SListPushFront(&plist, 1000); // 单链表单位头插法
SListPrint(plist); // 单链表的打印
}
void test2()
{
SLTNode* plist = NULL; // 创建头节点
SListPushBack(&plist, 0); // 单链表的尾插法
SListPushBack(&plist, 1); // 单链表的尾插法
SListPushBack(&plist, 2); // 单链表的尾插法
SListPopFront(&plist); // 单链表单位头插法
SListPrint(plist); // 单链表的打印
SListPushFront(&plist, 10); // 单链表的头插法
SListPushFront(&plist, 100); // 单链表的头插法;
SListPushFront(&plist, 1000); // 单链表的头插法;
SListPopFront(&plist); // 单链表的头删法;
SListPopFront(&plist); // 单链表的头删法
SListPopFront(&plist); // 单链表的头删法
SListPopFront(&plist); // 单链表的头删法
SListPopFront(&plist); // 单链表的头删法
SListPrint(plist); // 单链表的打印
}
void test3()
{
SLTNode* plist = NULL; // 创建头节点
SListPushBack(&plist, 0); // 单链表的尾插法
SListPushBack(&plist, 1); // 单链表的尾插法
SListPushBack(&plist, 2); // 单链表的尾插法
SListPrint(plist); // 单链表的打印
SListPopBack(&plist); // 单链表的尾删法
SListPrint(plist); // 单链表的打印
}
void test4()
{
SLTNode* plist = NULL; // 创建头节点
SListPushBack(&plist, 0); // 单链表的尾插法
SListPushBack(&plist, 1); // 单链表的尾插法
SListPushBack(&plist, 2); // 单链表的尾插法
SListPrint(plist); // 单链表的打印
SListInsert(&plist, 0, 99); // 单链表中在pos数值前插入数值节点
SListInsert(&plist, 1, 10); // 单链表中在pos数值前插入数值节点
SListInsert(&plist, 2, 99); // 单链表中在pos数值前插入数值节点
SListInsert(&plist, 3,99); // 单链表中在pos数值前插入数值节点
SListPrint(plist); // 单链表的打印
printf("********************\n");
SListErase_2(&plist, 0); // 单链表中删除pos数值的节点
SListErase_2(&plist, 2); // 单链表中删除pos数值的节点
SListErase_2(&plist, 10); // 单链表中删除pos数值的节点
SListPrint(plist); // 单链表的打印
SListPrint(plist); // 单链表的打印
SListSeek(plist, 0); // 单链表中删除pos数值的节点
SListSeek(plist, 1); // 单链表中删除pos数值的节点
}
int main()
{
test1();
test2();
test3();
printf("*********************\n");
test4();
return 0;
}
😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊
SList.c 的代码:🔍🔍🔍
- SLIst.c 文件放的是 关于 单链表 功能实现的有关的函数
- 😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊
#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h" // 注意:这是自定义的头文件,与书名号括起来的的头文件,
// 的查询方式是不一样的,
// 开辟节点空间
static SLTNode* BuySListNode(STLDataType num) // staitc 修饰的方法只能在该文件中使用,
{ // 实质上是改变了其方法的作用域
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
newnode->data = num;
newnode->next = NULL;
return newnode;
}
static SLTNode* SListFind(SLTNode* phead, STLDataType num)
{
while (phead != NULL) //地址不为空,存在节点
{
if (phead->data == num)
{
return phead; // 找到返回地址
}
phead = phead->next; // 移动指针;
}
return NULL; // 不存在,返回空(NULL)
}
void SListPrint(SLTNode* phend) // 打印单链表的数据
{
SLTNode* cur = phend; // 用 cur 指针来代替头指针的访问,尽量不要移动头指针
while (cur != NULL) // 当节点的为 NULL的时候,就表示后面没有数据了
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
/*这里注意:形参和实参的传值的,改变传递地址,而只是一级指针的
的地址,所以注意它,接受的是二级指针,保存一级指针的地址,*/
void SListPushBack(SLTNode** pphead, STLDataType num) // 单链表的尾插法
{
SLTNode* newnode = BuySListNode(num);
if (*pphead == NULL) // 当插入的是第一个节点的时候,我们直接,用头指针指向该的位置地址
{
*pphead = newnode;
}
else // 当插入的不是第一个节点的时候,我们找到最后一个节点NULL
{
SLTNode* tail = *pphead; // 同样使用一个变量,来代替头指针的访问,我们尽量不要头指针
while (tail->next != NULL) // 找到最后一个节点的位置
{
tail = tail->next; // 移动指针;
}
// 当跳出循环,找到了,插入
tail->next = newnode;
}
}
void SListPushFront(SLTNode** pphead, STLDataType num) // 单链表的头插法
{
SLTNode* newnode = BuySListNode(num); // 创建节点空间
newnode->next = *pphead; // 交互
(*pphead) = newnode;
}
void SListPopFront(SLTNode** pphead) // 单链表的头删法
{
SLTNode* next = (*pphead)->next; // 注意:不可以率先释放头指针的空间的,不然,你就丢失
// 个整个链表了。
free(*pphead); // 释放空间,
*pphead = next;
}
void SListPopBack(SLTNode** pphead) // 单链表的尾删法
{
/*1. 就是空的时候
2. 只有一个节点
3. 多个节点,主要就是要找到倒数第二个节点的位置*/
if (*pphead == NULL)
{
return;
}
else if( (*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{ // 前后指针,主要就是为了,定位到倒数第二个节点的位置
SLTNode* prev = NULL;
SLTNode* tail = (*pphead);
while (tail->next != NULL)
{
prev = tail;
tail = tail->next; // 移动指针
}
// 跳出循环,找到了,最后一个节点
free(tail);
tail = NULL;
// 把倒数第二个的节点置为 NULL;
prev->next = NULL;
}
}
// 单链表 pow 位置的插入法
void SListInsert(SLTNode** pphead,STLDataType x, STLDataType num)
{
SLTNode* pos = SListFind(*pphead, x); // 判断找寻该数值的节点,
if (pos) // 该数值的节点存在,在实行插入操作
{
if (pos == *pphead) // 插入位置位于头部,使用复用,头插法
{
SListPushFront(pphead, num); // 复用,头插法
}
else
{ // 主要:找到该数值的,前一个节点的位置,进行交互
SLTNode* newnode = BuySListNode(num); // 创建节点
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next; // 移动指针;
}
prev->next = newnode;
newnode->next = pos;
}
}
}
void SListErase(SLTNode** pphead, STLDataType num) // 删除数值为 num的节点
{
SLTNode* pos = SListFind(*pphead, num); // 找寻是否存在该数值的节点
if (pos) // 存在才,进行删除操作
{
if (pos == *pphead)
{
// 头删法 复用
SListPopFront(pphead); // 注意这里的参数是二级指针
}
else
{
// 前后指针 ,主要是为了;找到该数值的前面的节点,后进行交互
SLTNode* prev = *pphead;
SLTNode* tail = *pphead;
while (tail != pos)
{
prev = tail;
tail = tail->next;
}
prev->next = tail->next;
free(pos); // 释放空间
tail = NULL;
pos = NULL; // 手动置为空(NULL)
}
}
}
// 单链表中删除该数值位置的节点的 第二种方法,其中不同的就是找该数值,前的节点的位置的方法不同,而已
void SListErase_2(SLTNode** pphead, STLDataType num)
{
SLTNode* pos = SListFind(*pphead, num); // 同样判断该数值的位置是否存在
if (pos) // 存在才,执行操作,
{
if (pos == *pphead) // 位于头节点 ,复用
{
SListPopFront(pphead); // 复用,头删法
}
else // 找到该数值的前个位置的节点
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev ->next = pos->next;
free(pos); // 释放空间
pos = NULL; // 手动置为空
prev = NULL;
}
}
}
void SListSeek(SLTNode* phead, STLDataType num) // 查找该数值是否存在
{
SLTNode* tail = SListFind(phead, num); // 判断数值节点是否存在,
if (tail == NULL)
{
printf("该数值的节点不存在!\n");
}
else
{
printf("%d:该数值的节点存在\n",tail->data);
}
}
😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊
最后:😊😊😊
限于自身的水平,其中存在的错误,希望大家,给予指教,韩信点兵——多多益善 ,谢谢大家!后会有期,江湖再见!😊😊😊