三、函数定义
查找节点
//查找结点
SLTNode* SLTNodeFind(SLTNode* phead, SLTDataType x)
{
assert(phead);
SLTNode* pcur = phead;
while (pcur)
{
if (pcur->data == x)
{
return pcur;
}
pcur = pcur->next;
}
return NULL;
}
查找节点我们是通过看数据域来查找的,查找节点函数的返回值类型是SLTNode* 因为不涉及到链表的修改,我们只需要传值;同样地,首=首先要断言看链表是否为空;为了方便遍历链表,我们定义一个节点pcur,在循环中没经历一个节点时,就拿这个节点地数据域的值和传入的数据进行比较,直到最后一个几点,如果某个节点的数据域的值和传入的值相同,就返回这个节点,若是遍历完链表还是找不到,说明这个链表没有要查找的节点,那么就返回NULL。
相关测试:
指定位置之前插入数据
//在指定位置之前插入数据
void SLTInsertFront(SLTNode** pphead,SLTNode* pos, SLTDataType x)
{
assert(pphead && *pphead);
assert(pos);
if (pos == *pphead)
{
SLTPushFront(pphead, x);
}
else
{
SLTNode* newnode = SLTBuyNode(x);
SLTNode* pre = *pphead;
while (pre->next != pos)
{
pre = pre->next;
}
pre->next = newnode;
newnode->next = pos;
}
}
需要配合SLNodeFind一起使用。链表不能为空,指定的位置处的节点也不能为空;先看到else中的内容,若是指定的位置不是头节点,为传入的数据申请一个新节点,循环找到pos前的节点,让pre的next指针指向newnode,newnode的next指针指向pos,这样就让链表多了一个指定位置之前的节点;若是pos就是头节点此时不能直接走else里面的内容,因为pre找不到,最终会是空,此时直接引用头插方法。
相关测试:
指定位置之后插入数据
//在指定位置之后插入数据
void SLTInsertBack(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode = SLTBuyNode(x);
SLTNode* next = pos->next;
pos->next = newnode;
newnode->next = next;
}
不需要传头节点,因为不需要找到指定位置之前的节点,只需要pos和pos之后的节点,先把pos之后的节点给存起来,再让pos的next指向newnode,再让newnode的next指向原链表pos之后的节点。
删除指定位置的节点
//删除指定位置的数据
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead && *pphead);
assert(pos);
SLTNode* pre = *pphead;
SLTNode* next = pos->next;
if (pos == *pphead)
{
SLTPopFront(pphead);
}
else
{
while (pre->next != pos)
{
pre = pre->next;
}
free(pos);
pos = NULL;
pre->next = next;
}
}
同样地,如果pos就等于*pphead那么直接循环则找不到pre,这个时候直接调用头删的函数;若是正常情况下,则先找到pos之前的节点,再释放pos处的节点,最后让pre的next指向next的节点(也就是原链表中pro的next节点)。
删除指定位置之后的节点
//删除指定位置之后的数据
void SLTEraseAfter(SLTNode* pos)
{
assert(pos && pos->next);
SLTNode* del = pos->next;
pos->next = del->next;
free(del);
del = NULL;
}
我们需要找到pos下一个和pos下下个的节点,因为删除了pos之后的节点之后要让pos的next指向pos的下下个节点;若是直接先 (pos->next=pos->next->next)再(free(pos->next)),这样会错误地删除原本链表中pos的下下个节点;
为了避免这种错误,我们先定义一个del节点存放要删除的节点,再让pos的next指向pos的下下个节点,最后删除del节点,因为此时的del的定义是在改变pos地next之前进行的,所以del代表的就是原链表pos的下一个节点并且不会改变,我们此时直接删除del不会对新链表造成破坏,这样就实现了该函数功能。
销毁单链表
//销毁单链表
void SLTDestroy(SLTNode** pphead)
{
assert(pphead && *pphead);
SLTNode* pcur = *pphead;
//SLTNode* next = NULL;
while (pcur)
{
SLTNode* next = pcur->next;
free(pcur);
pcur = next;
}
*pphead = NULL;
}
销毁单链表需要对其节点一个一个释放;我们对下面这个链表进行销毁并且调试观察;
在链表尾插完之后在内存中是这样的:
在进行销毁的前一刻内存中是这样的:
销毁了三次之后:
销毁完节点并且让*pphead置为空之后:
如此就完成了单链表的销毁。