首先我们来看看头文件中要实现哪些函数来完成链表的增删查改。
在SList.h文件中
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
void SLTPrint(SLTNode* phead);
//尾插
void SLTPushBack(SLTNode** phead, SLTDataType x);
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);
//尾删
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
void SLTErase(SLTNode** phead, SLTNode* pos);
void SLTEraseAfter(SLTNode* pos);
void SListDestroy(SLTNode** pphead);
再在SList.c中我们来看看这些函数是如何实现的。
#include"SList.h"
void SLTPrint(SLTNode* phead)
{
SLTNode* pcur = phead;
while (pcur)
{
printf("%d->", pcur->data);
pcur = pcur->next;
}
printf("NULL\n");
}
SLTNode* SLTBuyNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail!");
exit(1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
//空链表和非空链表
SLTNode* newnode = SLTBuyNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLTNode* ptail = *pphead;
while (ptail->next)
{
ptail = ptail->next;
}
ptail->next = newnode;
}
}
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = SLTBuyNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
void SLTPopBack(SLTNode** pphead)
{
assert(pphead);
//链表不能为空
assert(*pphead);
SLTNode* prev = *pphead;
SLTNode* ptail = *pphead;
while (ptail->next)
{
prev = ptail;
ptail = ptail->next;
}
free(ptail);
ptail = NULL;
prev->next = NULL;
}
void SLTPopFront(SLTNode** pphead)
{
assert(pphead && *pphead);
SLTNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
SLTNode* pcur = phead;
while (pcur)
{
if (pcur->data == x)
{
return pcur;
}
pcur = pcur->next;
}
return NULL;
}
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(*pphead && pphead);
assert(pos);
SLTNode* newnode = SLTBuyNode(x);
//若pos == *pphead
if (pos == *pphead)
{
SLTPushFront(pphead, x);
}
SLTNode* prev = *pphead;
while (prev->next)
{
prev = prev->next;
}
newnode->next = pos;
prev->next = newnode;
}
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode = SLTBuyNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead && *pphead);
assert(pos);
if (pos == *pphead)
{
SLTPopFront(pphead);//头删是要传二级指针
}
SLTNode* prev = *pphead;
while(prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
void SLTEraseAfter(SLTNode* pos)
{
assert(pos&&pos->next);
SLTNode* del = pos->next;
pos->next = del->next;
free(del);
del = NULL;
}
void SListDestroy(SLTNode** pphead)
{
SLTNode* pcur = *pphead;
while (pcur)
{
while (pcur)
{
SLTNode* next = pcur->next;
free(pcur);
pcur = NULL;
}
}
*pphead = NULL;
}
最后在创建一个test.c文件来运行
#include"SList.h"
void SListTest01()
{
//创建几个节点
SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));
node1->data = 1;
SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));
node2->data = 2;
SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));
node3->data = 3;
SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));
node4->data = 4;
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = NULL;
SLTPushBack(node4, 6);
SLTNode* plist = node1;
SLTPrint(plist);
}
void SListTest02()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTPushFront(&plist, 5);
SLTPushFront(&plist, 6);
SLTPrint(plist);
SLTPopBack(&plist);//相当于尾删了一个4
SLTPrint(plist);
SLTNode* find = SLTFind(plist, 3);
//SLTInsert(&plist, find, 11);//在find位置之前插入11;
SLTInsertAfter(find, 11);
SLTPrint(plist);
SLTErase(&plist, find);
SLTPrint(plist);
SListDestroy(&plist);
SLTPrint(plist);
// if (find == NULL)
// {
// printf("没有找到");
// }
// else
// {
// printf("找到了");
// }
}
int main()
{
/*SListTest01();*/
SListTest02();
return 0;
}
这里链表在涉及到头指针的删除与改变时,我们要传二级指针,因为我们要传头指针的地址,才能改变头指针的值。
void SLTPopFront(SLTNode** pphead)
{
assert(pphead && *pphead);
SLTNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
在链表的实现中,也要注意指针释放时,能否还能找到被释放的指针指向的下一个元素,因此我们通常还得设置一个prev指针,假如pos为想要删去的位置,我们要在pre->next!=pos之前;将prev->next = pos->next,防止pos释放后找不到pos的下一个节点。
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead && *pphead);
assert(pos);
if (pos == *pphead)
{
SLTPopFront(pphead);//头删是要传二级指针}
SLTNode* prev = *pphead;
while(prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
我们再来看一张图,明白指针,二级指针表明的是什么
最后,完成一个链表的实现最重要的就是画图,多画图就知道如何实现一个链表的增删查改的一个思路了。最后,愿大家大学不挂科,写代码超级神!!!