前言
本文承接上篇对单链表的构建和删除插入,将要详细讲述有关单链表的查找销毁,以及如何在指定位置附近对数据进行添加和删除,本文中会用到上一篇blog中的相关函数,忘记的友友可以到上一篇查看~
必看:本文中所有代码的具体实现都可以在首页的gitee链接中免费查看获取!!!
一、查找
实现思路:由于单链表只能从表头开始一个一个往后找,所以在定义查找函数时,肯定要有指向表头的一级指针(此时表头直接指向链表的实际首元结点,即存储第一个数据元素的结点)和查找的结点中的数据;我们找到这个结点肯定要进行使用,因此我们返回的数据类型应该是一级指针类型(即返回结点的地址);查找成功我们就返回结点地址,找不到则返回NULL
SLTN* SLTFind(SLTN* phead, SLTdata x);
//在Slist.h头文件中声明
函数的具体实现:
注意:我们通常不直接使用头指针,而是重新定义一个变量pcur来实现遍历查找,此处是习惯,大多数时候是为了避免改变头指针而造成报错(具体后面会讲到)
//查找
SLTN* SLTFind(SLTN* phead, SLTdata x)//查找的节点中的值
{
SLTN* pcur = phead;
while (pcur)
{
if (pcur->data == x)
{
//printf("找到啦!\n");在测试函数中实现
//break;可以直接返回pcur
return pcur;
}
pcur = pcur->next;
}
return NULL;//将找到的数据的地址返回
}
下面我们在test.c中进行函数的测试
void test()
{
SLTN* plist = NULL;
//首先先向单链表中插入数据
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
//进行查找
SLTN* find=SLTFind(plist, 1);
if (find == NULL)
printf("没找到!\n");
else
printf("找到了\n");
}
二、销毁
函数实现思路:想要销毁单链表,肯定要利用到二级指针,这样才能够释放我们各个结点的内存(要改变结点的地址),此时我们的链表不能为空,二级指针同样也不能为空。要将所有的结点全部释放我们就要用到while循环(不清楚循环次数),最后要记得把表头置为NULL!
此处同样是出于习惯不直接改变头指针
//销毁链表(销毁一个一个结点)
void SListDesTory(SLTN** pphead)
{
assert(pphead && *pphead);
SLTN* pcur = *pphead;
while (pcur)
{
SLTN* next = pcur->next;
free(pcur);
pcur = next;
}
*pphead = NULL;
}
也可以对代码进行一下优化: 直接利用头指针进行销毁
void SListDesTory(SLTN** pphead)
{
assert(pphead && *pphead);
while (pphead)
{
SLTN* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
*pphead = NULL;
}
三、在指定位置处插入数据
-
在指定位置前插入
函数实现思路:此时我们先来考虑一种特殊情况,如果pos指向的是我们的首元素,那在pos之前插入肯定要改变我们头指针的指向,这时就必须用到指向头指针的指针;此时我们肯定也要将pos作为函数的参数因为我们得知道该结点具体的地址,最后再是我们需要插入的元素数据。
如图所示:此时这种情况插入数据==头插,我们可以直接引用头插函数
一般情况如下,知道pos的位置,但我们需要用到pos前一个结点的地址prev,才能实现结点的插入,因此我们得先利用循环找到pos的前一个结点
综上,函数实现如下
//在指定位置之前插入数据
void SLTInsert(SLTN** pphead, SLTN* pos, SLTdata x)
{
//首先pos不能为NULL,否则根本找不到这个位置,链表同样不能为空
assert(pos && pphead && *pphead);
SLTN*prev = *pphead;
//找到pos前面的节点
if (pos == *pphead)
{
//直接利用头插
SLTPushFront(pphead, x);
}
else
{
while ((prev)->next != pos)
prev = (prev)->next;
SLTN* newnode = SLBuyNode(x);//别忘记给新结点申请空间
prev->next = newnode;//将该位置的next存储新节点的地址
newnode->next = pos;
}
}
-
在指定位置后插入
函数实现思路:在指定位置后插入非常简单,pos后一个结点的地址我们是容易找到的,此时我们的函数参数,只需要pos以及需要插入的数据即可
//在指定位置之后插入数据
void SLTInsertafter(SLTN* pos, SLTdata x)
{
assert(pos);//pos不能为NULL
SLTN* newnode= SLBuyNode(x);
//注意此处的顺序不可调换,否则pos->next会被修改
newnode->next = pos->next;
pos->next = newnode;
}
四、在指定位置处删除数据
-
删除pos处的结点
函数实现思路:
首先先考虑特殊情况,假如单链表中只有一个结点,此时我们应该直接将头结点释放,则此时要改变头节点要用到二级指针;
一般情况下,我们只需要先将pos的前后两个结点链接起来,再将pos释放掉即可
//删除pos节点
void SLTErase(SLTN** pphead, SLTN* pos)
{
assert(pphead && *pphead && pos);
SLTN* prel = *pphead;
//分pos是头结点和不是头结点的情况
if (pos == *pphead)
{
*pphead= (*pphead)->next;//第二个结点要变成新的头结点
free(pos);//此时*pphead已经改变了指向,可以直接释放pos
}
else
{
while (prel->next != pos)
{
prel = prel->next;//首先找到pos前面的节点
}
prel->next = pos->next;//将pos前后两个结点相连
free(pos);
pos = NULL;//pos指针不再使用置为NULL
}
}
-
删除pos之后的结点
函数实现思路:找到pos之后的结点的地址,先将pos和删除结点之后的结点相连,再将删除处的结点释放,此时我们只需要用到pos即可
//删除pos之后的结点
void SLTEraseAfter(SLTN* pos)
{
assert(pos&&pos->next);//pos的下一个结点也不能为空,否则不能删除
SLTN* del = pos->next;
pos->next = del->next;//注意此处不能直接连等,否则后面找不到删除处结点的地址了!!!
free(del);
del = NULL;
}
总结
本文总结了单链表查找销毁,指定位置删除插入的一系列操作,如果有任何更好的方法和建议欢迎大家在评论区下方留言