c语言,单链表的实现----------有全代码!!!!

1.单链表的定义和结构

单链表是一种链式的数据结构,它用一组不连续的储存单元存反线性表中的数据元素。链表中的数据是以节点的形式来表示的,节点和节点之间相互连接

一般来说节点有两部分组成 1.数据域 :数据域用来存储各种类型的数据(浮点数,字符串,自定义类型的数据),2.指针域: 指针域用来存储的是指针,它用来指向下一个节点

 2.单链表的实现

SLlist.h

//定义单链表的节点
typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType Data;
	struct SListNode* next;//指向下一个节点的指针
}SLTNode;


//增加新的节点
SLTNode* SLTBuyNode(SLTDataType x);

//打印链表
void SLTPrint(SLTNode* phead);

//尾插
void SLTPushBack(SLTNode** pphead, 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);

//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);

//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);

//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);

//销毁链表
void SListDesTroy(SLTNode** pphead);

SLlist.c

//增加新的节点
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 SLTPrint(SLTNode* phead) {

    //循环打印,当phead指向NULL(也就是尾节点指向的下一个节点)时停止
	while (phead)
	{

		printf("%d->", phead->Data);
        //让phead指向下一个节点,并赋值给phead
		phead = phead->next;
	}
	printf("NULL\n");
}

//尾插
//想要修改值就要传地址,不能传值。而phead是个指针,我们要拿二级指针接收
void SLTPushBack(SLTNode** pphead, SLTDataType x) {


	assert(pphead);

	//*pphead说明第一个节点为NULL也就是说链表为空
	if (*pphead == NULL) 
	{
		*pphead = SLTBuyNode(x);    //直接创建一个新的节点
	}

    //链表不为NULL时尾插
	else
	{
		SLTNode* ptail = *pphead;    //创建一个节点,从头开始遍历找尾节点

        //遍历链表,找到尾节点
		while (ptail->next)    //下一个节点为NULL表达式为假,就说明已经找到了尾节点
		{
			ptail = ptail->next;
		}
			ptail->next = SLTBuyNode(x);   //找到为节点,让尾节点指向新的节点,完成尾插
	}

}

//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x) {
	assert(pphead);

	SLTNode* newnode = SLTBuyNode(x);    //创建一个新的节点准备进行头插

	newnode->next = *pphead;      //直接让新节点指向原先的第一个节点
	*pphead = newnode;            //让新的节点成为新的第一个节点

	//在是有一个节点的情况下也可以完美完成任务
}

//尾删
//可能要修改第一个节点,所以得传地址,用二级指针** pphead接收
void SLTPopBack(SLTNode** pphead) {
	//列表不能为NULL不然删啥
	assert(pphead && *pphead);

	SLTNode* ptail;    //找尾节点
	
	SLTNode* prev;    //尾节点前一个节点,它将来可能是新的尾节点
	prev = ptail = *pphead;

	//只有一个节点,直接删除不用找尾节点前面的节点
	if (ptail->next == NULL)
	{
		//改变头节点需要用到pphead,对它进行解引用得到原第一个节点
		free(*pphead);
		*pphead = NULL;
	}

    //有一个以上的节点,开始找尾节点,进行尾删
	else
	{
		//当 某个节点的下一个节点(指向)->NULL 时找到尾节点
		while (ptail->next)
		{
			prev = ptail;
			ptail = ptail->next;
		}
		
		free(ptail);    //找到了原尾节点直接释放
		ptail = NULL;   
		prev->next = NULL;    //让新的位节点(指向)->NULL
	}
}

//头删
//肯定会修改到第一个节点,要传地址,拿二级指针** pphead接收
void SLTPopFront(SLTNode** pphead) {
	assert(pphead && *pphead);

	SLTNode* ptail = *pphead; //记录原第一个节点

    //改变原第一个节点
	*pphead = ptail->next;   //让第二个节点成为,新的第一个节点

	//直接释放原第一个节点
	free(ptail);
	ptail = NULL;
}

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x) {
	//空链表的话不用找了
	assert(phead);

	SLTNode* pcur = phead;
	
	//让pcur遍历链表,直到尾节点指向的下一个节点NULL,说明没有这个元素
	while (pcur)
	{
		if(pcur->Data == x)
		{
			return pcur;    //有这个元素,直接返回这个元素所在节点的地址
		}
		pcur = pcur->next;
	}
	return NULL;
}

//在指定位置之前插入数据
//可能会改变第一个节点要传地址,拿二级指针** pphead接收
//pos 为指定位置
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) {

	assert(pphead);
    
    //创新的节点
	SLTNode* newnode = SLTBuyNode(x);

	SLTNode* prev = *pphead; //prev用找到pos之前的位置

	//如果只有一个节点,会出现prev找不到pos的情况,因为一开始这两的位置一样,prev的下一个位置永远不可能是pos
	if (pos == prev) 
	{
		SLTPushFront(pphead, x);
	}

    //有多个节点的情况,开始找pos节点并再此之前插入
	else 
	{
		//找pos之前的节点prev
		while (prev->next != pos)
		{
			prev = prev->next;
		}
        
        //拿1 -> 2 -> NULL 我们要在1(prev) 2(pos)之间插入3演示
		newnode->next = pos;     //先让新的节点,指向pos   3 -> 2 ->NULL
		prev->next = newnode;    //在让prev节点指向新的节点  1 -> 3 -> 2 ->NULL
	}    
}

//在指定位置之后插入数据
//不存在改变第一个节点的情况,传第一个节点值过来就够用
void SLTInsertAfter(SLTNode* pos, SLTDataType x) {
	//指定的位置不能为NULL,要不然谁知道你想要在谁之后插入数据
	assert(pos);
    
    创建新的节点
	SLTNode* newnode = SLTBuyNode(x);
	SLTNode* del = pos->next;  //del为pos之后的节点
	
	//如果只有一个节点,那么pos指向的下一个节点为NULL,这套逻辑依旧适用
    //拿1 -> 2 -> 3 -> NULL 我们要再2(pos)3(del)之间插入4

	newnode->next = del;  //先让新的节点指向del   4 ->3 -> NULL
	pos->next = newnode;  //在让pos指向新的节点   1 -> 2 -> 4 -> 3 -> NULL

}

//删除pos节点
//如果删除的是第一点,得传地址,因为第一个节点会发生变化
void SLTErase(SLTNode** pphead, SLTNode* pos) {
    
    //链表不能为NULL不然删啥
	assert(pphead && pos && *pphead);

	SLTNode* del = pos->next;   //del为pos之后的节点
	SLTNode* prev = *pphead;    //prev为pos之前的节点

	//如果只有一个节点,直接删即可,不用去找pos前一个节点,这样找找不到
	if (pos == del)//pos = del 我搞错了,这样就赋值了
	{
		//头删,传头结点的地址也就是pphead
		SLTPopFront(pphead);
	}

    //不止一个节点,找pos之前的节点prev
	else
	{
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		//把pos前一个节点prev和后一个节点del连接起来,在把pos节点释放
		prev->next = del;
		free(pos);
		pos = NULL;
	}
}

//删除pos之后的节点
//第一个节点不可能被修改,因为即使只有一个节点,第一个节点后面的节点也是NULL
void SLTEraseAfter(SLTNode* pos) {

	//pos后面的节点不能为NULL不然删啥
	assert(pos && pos->next);

	SLTNode* del = pos->next;    //del为pos之后的节点

	//1->2->3,让1跟3连接起来,在释放2
	pos->next = del->next;
	free(del);
	del = NULL;
}

//销毁链表
//涉及修改第一个,需要接收头结点地址
void SListDesTroy(SLTNode** pphead) {

	assert(pphead && *pphead);


	SLTNode* pcur = *pphead;     //pcur用来遍历链表,依次销毁
	SLTNode* next;     //next为当前需销毁节点的下一个节点,不然直接销毁就找不到下一个节点了
 
	//如果pcur为NULL,说明走到了尾节点指向的NULL,链表销毁完毕
	while (pcur)
	{
		next = pcur->next;	//先保存当前需要销毁节点的下一个节点的地址
		free(pcur);         //销毁当前节点
		pcur = next;        //走向下一个节点
	}

	*pphead = NULL;    //让第一个节点为NULL,链表销毁完毕后为NULL
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/552385.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于SpringBoot+Vue的疾病防控系统设计与实现(源码+文档+包运行)

一.系统概述 在如今社会上,关于信息上面的处理,没有任何一个企业或者个人会忽视,如何让信息急速传递,并且归档储存查询,采用之前的纸张记录模式已经不符合当前使用要求了。所以,对疾病防控信息管理的提升&a…

windows 如何安装 perl ?

链接:https://strawberryperl.com/ 我们选择安装 “草莓 perl” 下载后根据引导安装就行了

node.jd版本降级/升级

第一步.先清空本地安装的node.js版本 按健winR弹出窗口,键盘输入cmd,然后敲回车(或者鼠标直接点击电脑桌面最左下角的win窗口图标弹出,输入cmd再点击回车键) 进入命令控制行窗口,输入where node,查看本地…

双指针的引入和深入思考(持续更新中)

目录 1.引入双指针 2.使用场景 3.例题引入 1.引入双指针 当我们需要维护某个区间性质的或者是求满足某些性质的区间的长度时,对于一个区间是由左右端点的,我们有简单的枚举左右端点的O()的时间的做法,当时在大多数题目中是不可行的&#…

DataX案例,MongoDB数据导入HDFS与MySQL

【尚硅谷】Alibaba开源数据同步工具DataX技术教程_哔哩哔哩_bilibili 目录 1、MongoDB 1.1、MongoDB介绍 1.2、MongoDB基本概念解析 1.3、MongoDB中的数据存储结构 1.4、MongoDB启动服务 1.5、MongoDB小案例 2、DataX导入导出案例 2.1、读取MongoDB的数据导入到HDFS 2…

论文笔记:Does Writing with Language Models Reduce Content Diversity?

iclr 2024 reviewer评分 566 1 intro 大模型正在迅速改变人们创造内容的方式 虽然基于LLM的写作助手有可能提高写作质量并增加作者的生产力,但它们也引入了算法单一文化——>论文旨在评估与LLM一起写作是否无意中降低了内容的多样性论文设计了一个控制实验&…

Kubernetes部署应用利器Helm详解

文章目录 一、helm概述&安装1.为什么需要Helm2.Helm介绍3.Helm架构4.部署Helm客户端5.Helm基本使用5.1 创建Chart示例 二、Helm 应用部署、升级1.创建项目(chat所需目录、文件)2.创建/拷贝项目的yaml文件到templates目录下3.使用Helm进行部署项目4.H…

第十五届蓝桥杯复盘python大学A组——试题B 召唤数学精灵

按照正常思路解决,由于累乘消耗大量时间,因此这不是一个明智的解决方案。 这段代码执行速度非常慢的原因在于它试图计算非常大的数的阶乘(累乘),并且对于每一个i的值都执行这个计算。阶乘的增长是极其迅速的&#xff…

49.HarmonyOS鸿蒙系统 App(ArkUI)Tab导航组件的使用

HarmonyOS鸿蒙系统 App(ArkUI)Tab导航组件的使用 图片显示 Row() {Image($r(app.media.leaf)).height(100).width(100)Image($r(app.media.icon)).height(100).width(100) } 左侧导航 import prompt from ohos.prompt; import promptAction from ohos.promptAction; Entry C…

vue2知识点1 ———— (vue指令,vue的响应式基础)

vue2的知识点,更多前端知识在主页,还有其他知识会持续更新 Vue 指令 Vue指令是Vue.js中的一个重要概念,用于向DOM元素添加特定行为或功能。Vue指令以v-开头,例如v-bind、v-if、v-for等。 v-bind 动态绑定属性 用法&#xff1a…

windows ubuntu 子系统:肿瘤全外篇,2. fq 数据质控,比对。

首先我们先下载一组全外显子测序数据。nabi sra库,随机找了一个。 来自受试者“16177_CCPM_1300019”(SRR28391647, SRR28398576)的样本“16177_CCPM_1300019_BB5”的基因组DNA配对端文库“0369547849_Illumina_P5-Popal_P7-Hefel”的Illumina随机外显子测序 下载下…

SGI_STL空间配置器源码剖析(一)总览

SGI 全称为 Silicon Graphics [Computer System] Inc. 硅图[计算机系统] 公司,SGI_STL是SGI实现的C的标准模板库。 SGI STL的空间配置器包括一级和二级两种。 一级空间配置器allocator采用malloc和free来管理内存,这与C标准库中提供的allocator是相似的…

VS集成vcpkg

VS集成vcpkg 下载vcpkg 下载vcpkg git clone https://github.com/Microsoft/vcpkg.git安装vcpgk,文件目录 .\bootstrap-vcpkg.bat集成到vs2022中 # 集成到项目 vcpkg integrate project vcpkg integrate installPS C:\Users\Administrator> vcpkg integrate…

大模型开发轻松入门——(1)从搭建自己的环境开始

pip install openai import openai import osfrom dotenv import load_dotenv, find_dotenv _ load_dotenv(find_dotenv())openai.api_key os.getenv(OPENAI_API_KEY)

如何选择投资交易策略?很简单,只需回答fpmarkets6个问题

刚迈出交易的第一步的投资新手们,是不是还没有选择策略?外汇市场上的交易策略是一种算法,可以让投资者以最低的风险尽快实现目标。目标通常是获得一定比例的利润。 那么如何选择投资交易策略?很简单,只需回答fpmarkets…

计算机网络 2.2数据传输方式

第二节 数据传输方式 一、数据通信系统模型 添加图片注释,不超过 140 字(可选) 1.数据终端设备(DTE) 作用:用于处理用户数据的设备,是数据通信系统的信源和信宿。 设备:便携计算机…

酒店餐厅装水离子雾化壁炉前和装后对比

酒店餐厅装水离子雾化壁炉前和装后的对比可以体现出餐厅氛围和客户体验的显著改变: 装前: 普通的氛围:餐厅可能显得比较普通,缺乏特色或独特的装饰元素。 视觉上缺乏焦点:餐厅空间可能显得相对平淡,缺乏…

如何在MacOS上使用OpenHarmony SDK交叉编译?

本文以cJSON三方库为例介绍如何通过OpenHarmony的SDK在Mac平台进行交叉编译。 环境准备 SDK准备 我们可以通过 openHarmony SDK 官方发布渠道下载对应mac版本的SDK,当前OpenHarmony MAC版本的SDK有2种,一种是x86架构,另一种是arm64&#x…

【小白学机器学习13】一文理解假设检验的反证法,H0如何设计的,什么时候用左侧检验和右侧检验,等各种关于假设检验的基础知识

目录 前言: 目标 1 什么叫 假设检验 1.1 假设检验的定义 1.1.1 来自百度百科 1.1.2 维基百科 1.2 假设检验的最底层逻辑:是反证法思想 1.3 假设检验的底层构造:小概率反证法思想 2 什么叫反证法 2.1 反证法的概念 2.1.1 来自百度…

HarmonyOS开发实例:【任务延时调度】

介绍 本示例使用[ohos.WorkSchedulerExtensionAbility] 、[ohos.net.http]、[ohos.notification] 、[ohos.bundle]、[ohos.fileio] 等接口,实现了设置后台任务、下载更新包 、保存更新包、发送通知 、安装更新包实现升级的功能。 效果预览 使用说明 安装本应用之…