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

news2024/12/23 13:21:25

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
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入理解同步与异步编程及协程管理在Python中的应用

文章目录 1. 同步与异步函数的对比1.1 同步函数1.2 异步函数1.3 对比 2. 管理多个协程与异常处理2.1 并发执行多个协程2.2 错误处理2.3 任务取消 本文将探索Python中同步与异步编程的基本概念及其区别。还会详细介绍如何使用asyncio库来有效管理协程,包括任务的创建…

一文读懂uniapp中的tabBar底部导航

目录 1. 基本知识2. Demo 1. 基本知识 UniApp 中的 tabBar 是用来在应用程序底部显示可切换的选项卡的组件,通常用于实现底部导航栏 允许用户通过点击不同的选项卡来切换应用程序的不同页面或功能模块 其代码如下: "tabBar":{"color&q…

UOS系统-mips架构---Java环境安装

平时都是在windows系统上安装的java环境,今天需要在uos系统安装java1.8的环境,记录一下安装过程。 (以下均在root权限下运行) 一、查找java1.8 jdk版本 apt search openjdkopenjdk-8-jdk/未知,未知 1.8.0.212-2deepin mips64el O…

车载摄像头画质增强解决方案,赋能智能驾驶新时代

在智能化浪潮席卷汽车产业的今天,车载摄像头作为智能驾驶的“眼睛”,其画质清晰度直接关系到车辆感知环境的准确性和驾驶的安全性。然而,面对复杂多变的行车环境,如何确保车载摄像头在不同场景下都能呈现出高质量的图像&#xff0…

分布式调度平台xxl-job

1.xxl-job介绍 xxl-job 是一个轻量级分布式任务调度框架,支持动态添加、修改、删除定时任务,支持海量任务分片执行,支持任务执行日志在线查看和分页查询,同时支持任务失败告警和重试机制,支持分布式部署和高可用。xxl…

一文弄懂Seaborn绘制热力图

1. 引言 在本文中,我们将使用Seaborn库来以heatmap热力图的形式来表示数据。我们将重点介绍如何创建它,以及如何更改其颜色,调整对应字体大小等等。 闲话少说,我们直接开始吧! 2. 什么是热力图? Heatma…

Python 数学应用(四)

原文:zh.annas-archive.org/md5/123a7612a4e578f6816d36f968cfec22 译者:飞龙 协议:CC BY-NC-SA 4.0 第十一章:其他主题 在本章中,我们将讨论一些在本书前几章中没有涉及的主题。这些主题大多涉及不同的计算方式以及优…

向量数据库与图数据库:理解它们的区别

作者:Elastic Platform Team 大数据管理不仅仅是尽可能存储更多的数据。它关乎能够识别有意义的见解、发现隐藏的模式,并做出明智的决策。这种对高级分析的追求一直是数据建模和存储解决方案创新的驱动力,远远超出了传统关系数据库。 这些创…

C代码编译过程与进程内存分布

C代码编译过程 在这篇文章中,我们将探讨C语言代码的编译流程以及进程在运行时的内存布局。编译过程通常包括几个关键步骤:预处理、编译、汇编和链接。 预处理阶段主要是处理源代码文件中的宏定义、头文件包含和条件编译指令。在此阶段,编译…

ping命令的使用

一、实验环境 同实验案例分析ARP解析过程环境。 二、需求描述 熟悉 ping 命令的用法并熱悉 ping 命令的各种参数 三、推荐步骤 分别 ping 一个存在的和不存在的IP地址,观察返回的信息分别测试 ping 命令的相关参数。 四、实验步骤 1.ping 一个存在的和不存在…

工业电脑在ESOP工作站行业应用

ESOP工作站行业应用 项目背景 E-SOP是实现作业指导书电子化,并统一管理和集中控制的一套管理信息平台。信迈科技的ESOP终端是一款体积小巧功能齐全的高性价比工业电脑,上层通过网络与MES系统连接,下层连接显示器展示作业指导书。ESOP控制终…

FPGA - ZYNQ 基于EMIO的PS和PL交互

前言: Xilinx ZYNQ系列的芯片,GPIO分为 MIO 、EMIO、AXI_GPIO三种方式。 MIO :固定管脚,属于PS端,也就是ARM端。 EMIO :通过PL扩展,使用时需要分配PL(FPGA)管脚,消耗PL端资源。…

redis-plus-plus的安装与使用

文章目录 一、安装第一步:安装hiredis第二步:安装redis-plus-plus第三步:将编译后的可执行文件移动到/usr/local对应目录第四步:更新动态库 二、使用第一步:编写示例代码第二步:编译运行 本文参考自 redis-…

Pytest测试用例中的mark用法(包含代码示例与使用场景详解)

在软件开发中,测试是确保代码质量和功能稳定性的重要环节。Python作为一门流行的编程语言,拥有丰富的测试工具和框架,其中pytest是其中之一。pytest提供了丰富的功能来简化测试用例的编写,其中的mark功能允许我们对测试用例进行标…

Pytest精通指南(16)利用skip、skipif跳过用例执行

文章目录 前言skip源码分析skip装饰方法skip装饰类skip装饰模块skipif源码分析skipif装饰方法skipif装饰类skipif装饰模块拓展-用例内部跳过执行 前言 skip: skip用于无条件地跳过测试用例,无论测试环境的状态或条件如何。通常用于那些在任何情况下都不应该执行的测…

idea使用plantuml插件报错(类图):Dot Executable: /opt/local/bin/dot

报错提示: 解决方式: 方式一: 直接设置Remote Rendering即可 (使用服务器地址) 无特殊要求可直接使用默认提供的服务地址,也可自行搭建服务替换地址。 自行搭建服务可参考: 在本地Windows 11 系统的桌面…

分布式调度器

xxl-job介绍 xxl-job 是一个轻量级分布式任务调度框架,支持动态添加、修改、删除定时任务,支持海量任务分片执行,支持任务执行日志在线查看和分页查询,同时支持任务失败告警和重试机制,支持分布式部署和高可用。xxl-j…

中文编程入门(Lua5.4.6中文版)第十三章 Lua 文件操作

在《Lua世界》的冒险旅途中,勇士们时常需要与神秘的文本卷轴打交道。为了更好地掌握这些知识宝藏,Lua I/O库提供了两种强大的探索模式:简单模式和完全模式,助你轻松应对各类文献挑战。 简单模式:初识卷轴 简单模式如…

结构体及应用;结构体指针及应用;union、enum、typedef三个关键字

结构体及应用 参考文章链接:https://blog.csdn.net/zw1996/article/details/53844585结构体的声明 结构体的初始化 注意如果在定义结构体变量的时候没有初始化,那么后面就不能全部一起初始化了。 /这样是可以的,在定义变量的时候就初始化了…

顺序表(增删减改)+通讯录项目(数据结构)+顺序表专用题型

什么是顺序表 顺序表和数组的区别 顺序表本质就是数组 结构体初阶进阶 系统化的学习-CSDN博客 简单解释一下,就像大家去吃饭,然后左边是苍蝇馆子,右边是修饰过的苍蝇馆子,但是那个好看的苍蝇馆子一看,这不行啊&a…