C语言——单链表实现数据增删查改

devtools/2025/1/16 10:01:12/

一.前言

 嗨嗨嗨,我们又见面了。前面我们已经学习了关于数据结构中的顺序表,今天我们来学习数据结构中的单链表。废话不多说让我们直接开始吧。

二.正文

1.1链表的概念

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表的结构跟火车的车厢相似,淡季时车次的车厢会相应减少,旺季时车厢会额外增加几节。只需要将火车里的某节车厢去掉/加上,不会影响其他车厢,每节车厢都是独立存在的。

车厢是独立存在的,且每节车厢都有车门。想象一下这样的场景,假设每节车厢的车门都是锁上的状态,需要不同的钥匙才能解锁,每次只能携带一把钥匙的情况下如何从车头走到车尾?

最简单的做法:每节车厢里都放下一把车厢的钥匙。

在链表里,每节“车厢”是什么样的呢?

与顺序表不同的是,链表里的每节“车厢”都是独立申请下来的空间,我们称之为“节点/结点”

节点的组成主要有两部分:当前节点要保存的数据和保存下一个节点的地址。

图中指针变量plist保存的是第一个节点的地址,我们称plist此时“指向”第一个节点,如果我们希望plist“指向”第二个节点时,只需要修改plist保存的内容为0x0012FFA0。

为什么还需要指针变量来保存下一个节点的位置?

链表中每个节点都是独立申请的(即需要插入数据时采取申请一块节点的空间),我们需要通过指针变量来保存下一个节点位置才能从当前节点找到下一个节点。

1.2单链表的实现

SList.h(用来提前声明函数存在)

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef  int SLTDataType;
typedef struct SListNode//节点的结构由数据data和下一个节点的地址next组成
{SLTDataType data;struct SList* next;
}SLTNode;
void SLTPrint();//打印链表
void SLTPushBack();//尾插
void SLTPushFront();//头插
void SLTPopBack();//尾删
void SLTPopFront();//头删
SLTNode* SLTFind();//查找
void SLTInsertBefore();//在指定位置之前插入数据
void SLTInsertAfter();//在指定位置之后插入数据
void SLTErase();//在指定位置删除节点
void SLTEraseAfter();//在指定位置之后删除节点
void SLTDestroy();//销毁链表

SList.c(用来实现函数功能)

#include"SList.h"
SLTNode* SLTBuyNode(SLTDataType x)//创造节点
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail!");//return;exit(1);}else{newnode->next = NULL;newnode->data = x;return newnode;}
}
void SLTPushBack(SLTNode** pphead,SLTDataType x)//尾插
{assert(pphead);SLTNode* newnode= SLTBuyNode(x);if (*pphead == NULL){*pphead = newnode;}else{//找尾结点SLTNode* ptail = *pphead;while(ptail->next)//疑惑点为什么不是*pphead->next{ptail = ptail->next;}/*	while((*pphead)->next){}*/ptail->next = newnode;}
}
void SLTPrint(SLTNode* phead)//链表打印
{SLTNode* pcur = phead;while (pcur){printf("%d->", pcur->data);pcur=pcur->next;}printf("NULL\n");
}
void SLTPushFront(SLTNode** pphead,SLTDataType x)//头插
{assert(pphead);SLTNode* newnode = SLTBuyNode(x);newnode->next = *pphead;*pphead = newnode;
}
void SLTPopBack(SLTNode** pphead)//尾删
{assert(pphead && *pphead);if ((*pphead)->next==NULL){free(*pphead);*pphead = NULL;}else{SLTNode* ptail;SLTNode* pcur;ptail = pcur = *pphead;while (ptail->next){pcur = ptail;ptail = ptail->next;}free(ptail);ptail = NULL;pcur->next=NULL;}
}
void SLTPopFront(SLTNode** pphead)//头删
{assert(pphead && *pphead);SLTNode* pcur=*pphead;*pphead = (*pphead)->next;free(pcur);
}
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)//查找
{SLTNode* pcur = phead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}
void SLTInsertBefore(SLTNode** pphead,SLTNode* pos,SLTDataType x)//在指定位置前插入节点
{assert(pphead && *pphead);assert(pos);SLTNode* newnode = SLTBuyNode( x);//先找到目标位置的前一个节点SLTNode* prev = *pphead;if (pos==*pphead){SLTPopFront(pphead);}else{while (prev->next != pos){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);SLTNode* prev = *pphead;if (pos == *pphead){SLTPopFront(pphead);}else{while (prev->next != pos){prev = prev->next;}SLTNode* next = pos->next;prev->next = next;free(pos);pos = NULL;}
}
void SLTEraseAfter(SLTNode* pos)//删除指定位置之后的节点
{assert(pos&&pos->next);SLTNode* next = pos->next;pos->next = next->next;free(next);next = NULL;
}
void SLTDestroy(SLTNode** pphead)//销毁链表
{assert(pphead&&*pphead);SLTNode* next = *pphead;SLTNode* pcur = *pphead;while (pcur){next = (pcur)->next;free(pcur);//	pcur = NULL;pcur = next;//pcur = next;}*pphead = NULL;
}

test.c(用来测试所写代码功能是否正确)

#include"SList.h"
//void test1()//测试尾插和尾删
//{
//	SLTNode* plist = NULL;
//	SLTPushBack(&plist, 1);
//	SLTPushBack(&plist, 2);
//	SLTPushBack(&plist, 3);
//	SLTPushBack(&plist, 4);
//	SLTPopBack(&plist);
//	SLTPrint(plist);
//}
//void test2()//测试头插
//{
//	SLTNode* plist;
//	//plist = NULL;
//	SLTPushFront(&plist, 4);
//	SLTPushFront(&plist, 3);
//	SLTPushFront(&plist, 2);
//	SLTPushFront(&plist, 1);
//	SLTPrint(plist);
//}//void test3()//测试头删
//{
//	SLTNode* plist =NULL;
//	SLTPushBack(&plist, 1);
//	SLTPushBack(&plist, 2);
//	SLTPushBack(&plist, 3);
//	SLTPushBack(&plist, 4);
//	SLTPopFront(&plist);
//	SLTPrint(plist);
//}
//void test4()//测试查找
//{
//	SLTNode* plist = NULL;
//		SLTPushBack(&plist, 1);
//		SLTPushBack(&plist, 2);
//		SLTPushBack(&plist, 3);
//		SLTPushBack(&plist, 4);
//		SLTNode* find = SLTFind(plist, 4);
//}
//void test5()//测试在指定位置前插入节点
//{
//	SLTNode* plist = NULL;
//			SLTPushBack(&plist, 1);
//			SLTPushBack(&plist, 2);
//			SLTPushBack(&plist, 3);
//			SLTPushBack(&plist, 4);
//			SLTNode* find = SLTFind(plist, 3);
//			SLTInsertBefore(&plist, find, 5);
//			SLTPrint(plist);
//}
//void test6()//测试在指定位置之后插入节点
//{
//		SLTNode* plist = NULL;
//			SLTPushBack(&plist, 1);
//			SLTPushBack(&plist, 2);
//			SLTPushBack(&plist, 3);
//			SLTPushBack(&plist, 4);
//			SLTNode* find = SLTFind(plist, 4);
//			SLTInsertAfter(find, 5);
//			SLTPrint(plist);
//}
//test7()//测试在指定位置删除节点
//{
//	SLTNode* plist = NULL;
//	SLTPushBack(&plist, 1);
//	SLTPushBack(&plist, 2);
//	SLTPushBack(&plist, 3);
//	SLTPushBack(&plist, 4);
//	SLTNode* find = SLTFind(plist, 1);
//	SLTErase(&plist, find);
//	SLTPrint(plist);
//}
//void test8()//测试在指定位置之后删除节点
//{
//	SLTNode* plist = NULL;
//	SLTPushBack(&plist, 1);
//	SLTPushBack(&plist, 2);
//	SLTPushBack(&plist, 3);
//	SLTPushBack(&plist, 4);
//	SLTNode* find = SLTFind(plist, 1);
//	SLTEraseAfter(find);
//	SLTPrint(plist);
//}
void test9()//测试销毁链表
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);//SLTPushBack(&plist, 3);//SLTPushBack(&plist, 4);SLTDestroy(&plist);SLTPrint(plist);
}
int main()
{//test1();//test2();//test3();//test4();//test5();//test6();//test7();//test8();test9();return 0;
}

值得注意的是:上面的test.c只是本人在写单链表的时候测试所写函数功能能否跑得起来而所写的,大家也可以按自己的习惯来测试函数功能,上面代码仅供参考。

三.结文

今天的分享就到此结束了,集帅、集美们咱们下期不见不散~


http://www.ppmy.cn/devtools/26492.html

相关文章

【LeetCode】拓扑排序——课程表 I II

拓扑排序&#xff1a; AOV网&#xff1a;若用DAG图&#xff08;有向无环图&#xff09;表示一个工程&#xff0c;其顶点表示活动&#xff0c;用有向边<Vi, Vj>表示活动Vi必须先于活动Vj进行的这样一种关系&#xff0c;则将这种有向图称为顶点表示活动的网络&#xff0c;…

GPT每日面试题—csrf攻击的原理和解决方案

充分利用ChatGPT的优势&#xff0c;帮助我们快速准备前端面试。今日问题&#xff1a;csrf原理和解决方案? Q&#xff1a;如果在前端面试中&#xff0c;被问到csrf原理和解决方案&#xff0c;怎么回答比较好&#xff0c;全面具体的描述一下 A&#xff1a;在前端面试中&#xf…

商城系统推荐,如何找到一款可靠的商城系统?

如今&#xff0c;电商系统成为商家必不可少的营销工具&#xff0c;其系统在金融、外贸、零售等行业领域应用广泛。那么&#xff0c;作为初试水的企业又没有挑选电商系统的经验&#xff0c;如何找到拥有全功能、全渠道、可靠的网上商城系统呢&#xff1f; 我们可以先按电商系统…

《QT实用小工具·四十三》历史编辑器(支持历史搜索 关键字匹配)

1、概述 源码放在文章末尾 该项目实现了在输入框中输入部分信息能全部展现之前的历史输入信息&#xff0c;支持历史搜索和关键词匹配&#xff0c;项目demo演示如下所示&#xff1a; 项目部分代码如下所示&#xff1a; #include "historymodel.h" #include <QM…

数据库基础--MySQL简介以及基础MySQL操作

数据库概述 数据库&#xff08;DATABASE&#xff0c;简称DB&#xff09; 定义:是按照数据结构来组织、存储和管理数据的仓库.保存有组织的数据的容器(通常是一个文件或一组文件) 数据库管理系统(Database Management System,简称DBMS) 专门用于管理数据库的计算机系统软件;…

IP模块——计算机网络

IP模块在计算机网络中通常指的是处理互联网协议&#xff08;Internet Protocol&#xff0c;简称IP&#xff09;的部分&#xff0c;它负责网络中的数据包的发送和接收。IP是一种无连接的协议&#xff0c;意味着它不需要建立持久的连接才能在网络中传输数据。IP模块的主要任务包括…

社交媒体之谜:深度解析Facebook的内容策略

作为全球最大的社交媒体平台之一&#xff0c;Facebook在内容策略方面一直处于行业的领先地位。其内容策略不仅影响着数十亿用户的信息获取和社交互动&#xff0c;也深刻影响着整个社会的舆论和文化传播。本文将深入探讨Facebook的内容策略&#xff0c;剖析其背后的运作机制和对…

深度学习之视觉特征提取器——LeNet

LeNet 引入 LeNet是是由深度学习巨头Yann LeCun在1998年提出&#xff0c;可以算作多层卷积网络在图像识别领域的首次成功应用。我们现在通常说的LeNet是指LeNet-5&#xff0c;最早的LeNet-1在1988年即开始研究&#xff0c;前后持续十年之久。但是&#xff0c;受限于当时计算机…