单链表<数据结构 C版>
目录
概念
链表的单个结点
链表的打印操作
新结点的申请
尾部插入
头部插入
尾部删除
头部删除
查找
在指定位置之前插入数据
在任意位置之后插入数据
测试运行一下:
删除pos结点
删除pos之后结点
销毁链表
概念
单链表是一种在物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接顺序实现的。
链表的每个结点有两个部分,分别是数据和指向下个结点的指针,每个链表的最后一个结点的下一个结点为NULL(不能对NULL解引用)。
放一张bit课件里的图,我觉得很形象:
链表的单个结点
typedef int SLDataType;//重定义一下在链表内存放的数据类型,方便后期对类型进行统一修改
//链表的单个结点
typedef struct SListNode {//Single List Node :链表结点
SLDataType data;//存放的数据
struct SListNode* next;//指向下一个结点的指针
}SLNode;//重定义名字方便后期使用
链表的打印操作
//链表的打印操作
void SLPrint(SLNode* phead) {
assert(phead);//不能传入空指针
SLNode* pcur = phead;
//pointer cursor:指针光标,不让头结点丢失(虽然不会改变头结点的指向)
while (pcur) {//等同于pcur!=NULL
printf("%d->", pcur->data);//打印此结点的数据
pcur = pcur->next;//使pcur指向下一个结点
}
printf("NULL\n");
}
新结点的申请
后面会涉及到新结点的插入,申请新结点可以封装成一个函数,避免代码冗余
//新结点的申请
SLNode* SLBuyNode(SLDataType x) {
SLNode* node = (SLNode*)malloc(sizeof(SLNode));
if (!node) {//返回值为空,申请失败(一般是空间不足了),直接退出
perror("malloc fail");
exit(1);
}
node->data = x;//将数据赋给data
node->next = NULL;//将下一个节点置为NULL;
return node;
}
尾部插入
//尾部插入
void SLPushBack(SLNode** pphead, SLDataType x) {
//注意在这里我们传参的是二级指针,因为我们需要在函数内部改变头结点的指向
assert(pphead);//不能传NULL
//新结点的申请
SLNode* node=SLBuyNode(x);
if (*pphead == NULL) {
*pphead = node;
}
else {
SLNode* pcur = *pphead;
while (pcur->next) {//找到next元素为NULL的结点,也就是为链表尾部
pcur = pcur->next;
}
pcur->next = node;
}
}
测试运行一下:
头部插入
//头部插入
void SLPushFront(SLNode** pphead, SLDataType x) {
assert(pphead);
SLNode* node = SLBuyNode(x);
//这里需要处理头结点为空的情况吗?不需要,因为没有涉及到解引用其元素
node->next = *pphead;
*pphead = node;
}
尾部删除
//尾部删除
void SLPopBack(SLNode** pphead) {
assert(*pphead && pphead);//
if (!(*pphead)->next) {//处理只有一个元素的情况
free(*pphead);
*pphead = NULL;
}
else {
SLNode* prev = NULL;
SLNode* pcur = *pphead;
while (pcur->next) {//找到尾结点前一个结点
prev = pcur;
pcur = pcur->next;
}
free(pcur);//将尾结点释放
pcur = NULL;
prev->next = NULL;//将尾结点的前一个结点的next元素置为NULL
}
}
测试运行一下:
头部删除
//头部删除
void SLPopFront(SLNode** pphead) {
assert(*pphead && pphead);
SLNode* next = (*pphead)->next;//保存头结点的下一个结点地址
free(*pphead);//释放头结点
*pphead = next;//使头结点指向下一个结点
}
测试运行一下:
查找
在链表中想要对指定位置进行操作不能使用下标,所以我们必须找到指定位置的地址才能对其进行操作。
//查找
SLNode* SLFind(SLNode* phead, SLDataType x) {
assert(phead);
SLNode* pcur = phead;
while (pcur) {
if (pcur->data == x) {
return pcur;
}
pcur = pcur->next;
}
return NULL;
}
在指定位置之前插入数据
//在指定位置之前插入数据
void SLInsert(SLNode** pphead, SLNode* pos, SLDataType x) {
assert(pphead && pos && *pphead);//pos和pphead不能为空,以及pphead指向空间不能为空
if (*pphead == pos) {//处理头插的情况
SLPushFront(pphead,x);
}
else {
SLNode* node = SLBuyNode(x);
SLNode* pcur = *pphead;
while (pcur->next != pos) {//找到pos前一个结点
pcur = pcur->next;
}
node->next = pcur->next;
pcur->next = node;
}
}
测试运行一下:
在任意位置之后插入数据
//在任意位置之后插入数据
void SLInsertAfter(SLNode* pos, SLDataType x) {
assert(pos);//pos不能为空
SLNode* node = SLBuyNode(x);
node->next = pos->next;
pos->next = node;
}
测试运行一下:
删除pos结点
//删除pos结点
void SLErase(SLNode** pphead, SLNode* pos) {
assert(*pphead && pphead && pos);
if (*pphead == pos) {//处理头删的情况
SLPopFront(pphead);
}
else {
SLNode* pcur = *pphead;
while (pcur->next!= pos) {
pcur = pcur->next;
}
pcur->next = pos->next;
free(pos);
pos = NULL;
}
}
测试运行一下:
删除pos之后结点
//删除pos之后结点
void SLEraseAfter(SLNode* pos) {
assert(pos && pos->next);//pos后面的结点也不能为空
SLNode* next = pos->next;
pos->next = next->next;
free(next);
next = NULL;
}
测试运行一下:
销毁链表
//销毁链表
void SLDestory(SLNode** pphead) {
assert(pphead && *pphead);
SLNode* pcur = *pphead;
while (pcur) {
SLNode* nextnode = pcur->next;//使用一个变量接受下一个结点地址
free(pcur);
pcur = nextnode;
}
*pphead = NULL;
}
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!









