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

一.前言

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

二.正文

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只是本人在写单链表的时候测试所写函数功能能否跑得起来而所写的,大家也可以按自己的习惯来测试函数功能,上面代码仅供参考。

三.结文

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

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

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

相关文章

【C#】基础知识

0.参考 C#语言入门详解 1.几种打印hello_world的方式 1.1 console控制台 新建一个console&#xff0c;直接打印&#xff1a; Console.WriteLine("Hello_world");启动一闪而过&#xff0c;在vs调试中选择开始执行不调试&#xff08;without debug&#xff09;。 …

算法效率的判断及一些典型例题的讲解

一.算法效率 1.用处&#xff1a;判断算法的好坏&#xff0c;好的算法应该是高效的 2算法效率取决于时间复杂度和空间复杂度 <1>时间复杂度 1.1概念&#xff1a;算法中基本操作的执行次数就是算法的时间复杂度 1.2表示&#xff1a;大O的渐进表示法&#xff0c;例如O(N)…

java技术栈快速复习02_前端基础知识总结

前端基础 经典三件套&#xff1a; html&#xff08;盒子&#xff09;css&#xff08;样式&#xff09;JavaScript&#xff08;js&#xff1a;让盒子动起来&#xff09; html & css HTML全称&#xff1a;Hyper Text Markup Language(超文本标记语言)&#xff0c;不是编程语…

记一次生产事故的排查和解决

一. 事故概述 春节期间, 生产系统多次出现假死不可用现象, 导致绝大部分业务无法进行. 主要表现现象为接口无法访问. 背景为900W客户表和近实时ES, 以及春节期间疫情导致的普通卖菜场景近似秒杀等. 二. 排查过程 优先排查了info, error, catalina日志, 发现以下异常: 主要的…

边循环边删除List中的数据

List边循环&#xff0c;边删除&#xff1b;这种一听感觉就像是会出问题一样&#xff0c;其实只要是删除特定数据&#xff0c;就不会出问题&#xff0c;你如果直接循环删除所有数据&#xff0c;那可能就会出问题了&#xff0c;比如&#xff1a; public static void main(String[…

公考学习平台|基于SprinBoot+vue的公考学习平台(源码+数据库+文档)

公考学习平台目录 目录 基于SprinBootvue的公考学习平台 一、前言 二、系统设计 三、系统功能设计 5.1用户信息管理 5.2 视频信息管理 5.3公告信息管理 5.1论坛信息管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&…

React的useEffect

概念&#xff1a;useEffect是一个React Hook函数&#xff0c;组件渲染之后执行的函数 参数1是一个函数&#xff0c;可以把它叫做副作用函数&#xff0c;在函数内部可以放置要执行的操作参数2是一个数组&#xff08;可选参&#xff09;&#xff0c;在数组里放置依赖项&#x…

Liunx发布tomcat项目

Liunx在Tomcat发布JavaWeb项目 1.问题2.下载JDK3.下载Tomcat4.Tomcat本地JavaWeb项目打war包、解压、发布5.重启Tomcat,查看项目 1.问题 1.JDK 与 Tomcat 版本需匹配&#xff0c;否则页面不能正确显示 报错相关&#xff1a;Caused by: java.lang.ClassNotFoundException: java…

十一、大模型-Semantic Kernel与 LangChain 的对比

Semantic Kernel 与 LangChain 的对比 Semantic Kernel 和 LangChain 都是用于开发基于大型语言模型&#xff08;LLM&#xff09;的应用程序的框架&#xff0c;但它们各有特点和优势。 基本概念和目标 Semantic Kernel 是一个由微软开发的轻量级 SDK&#xff0c;旨在帮助开发…

每日OJ题_DFS爆搜深搜回溯剪枝⑤_力扣37. 解数独

目录 力扣37. 解数独 解析代码 力扣37. 解数独 37. 解数独 难度 困难 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的…

C++--const成员及const取地址操作符重载

前言 今天我们来了解一下const成员的基本使用,以及const取地址重载的运用 来开始今天的学习 const成员 1.基本定义, 将const修饰的“成员函数”称之为const成员函数&#xff0c;const修饰类成员函数&#xff0c;实际修饰该成员函数 隐含的*this指针&#xff0c;表明在该成员函…

Android4.4真机移植过程笔记(二)

5、盘符挂载 先定义overlay机制路径&#xff0c;后面storage_list.xml要用到&#xff1a; 在路径&#xff1a; rk3188_android4.4.1/device/rockchip/OK1000/overlay/frameworks/base/core/res/res/xml/定义好&#xff0c;注意名字要和emmc的代码片段&#xff08;往下面看&am…

python学习笔记----安装pycharm(1)

一、安装pycharm 1. 下载并安装pycharm https://www.jetbrains.com/pycharm/download2.汉化pycharm 安装插件并重启IDE完成汉化 二、 第一个python程序

Flask教程1:flask框架基础入门,路由、模板、装饰器

文章目录 一、 简介二、 概要 一、 简介 Flask是一个非常小的Python Web框架&#xff0c;被称为微型框架&#xff1b;只提供了一个稳健的核心&#xff0c;其他功能全部是通过扩展实现的&#xff1b;意思就是我们可以根据项目的需要量身定制&#xff0c;也意味着我们需要学习各…

分享天某云对象存储开发的体验

最近体验了天某云对象存储的功能&#xff0c;作为一名资深开发者&#xff0c;开发体验差强人意&#xff0c;与阿里云存在一定的差距。 首先在开发文档上居然没有基于nodejs的代码示例&#xff0c;只有java,c#,go等的代码示例&#xff0c;虽然有javascript的&#xff0c;但那也只…

Outlook大附件插件 有效解决附件大小限制问题

很多企业都是使用Outlook来进行邮件的收发&#xff0c;可是由于附件大小有限&#xff0c;导致很多大文件发不出去&#xff0c;就会产生Outlook大附件插件这种业务需求。 邮件系统在发送大文件时面临的限制问题主要如下&#xff1a; 1、附件大小限制&#xff1a;大多数邮件服务…

net lambda 、 匿名函数 以及集合(实现IEnumerable的 如数组 、list等)

匿名函数&#xff1a;》》》 Action a1 delegate(int i) { Console.WriteLine(i); }; Lambda:>>> Aciont a1 (int i) > { Console.WriteLine(i); }; 可以简写 &#xff08;编译器会自动根据委托类型 推断&#xff09; Action a1 &#xff08;i&#xff09;> {…

前端vite+rollup前端监控初始化——封装基础fmp消耗时间的npm包并且发布npm beta版本

文章目录 ⭐前言&#x1f496;vue3系列文章 ⭐初始化npm项目&#x1f496;type为module&#x1f496;rollup.config.js ⭐封装fmp耗时计算的class&#x1f496;npm build打包class对象 ⭐发布npm的beta版本&#x1f496; npm发布beta版本 ⭐安装web-performance-tool的beta版本…

springcloud自定义全局异常

自行创建一个实体类 /*** 全局异常处理类**/ ControllerAdvice public class GlobalExceptionHandler {ExceptionHandler(Exception.class) ResponseBody public Result error(Exception e){e.printStackTrace(); return Result.fail();}/*** 自定义异常处理方法* param e * re…

GPU 架构与 CUDA 关系 并行计算平台和编程模型 CUDA 线程层次结构 GPU 的算力是如何计算的 算力峰值

GPU 架构与 CUDA 关系 本文主要包含 NVIDIA GPU 硬件的基础概念、CUDA(Compute Unified Device Architecture)并行计算平台和编程模型,详细讲解 CUDA 线程层次结构,最后将讲解 GPU 的算力是如何计算的,这将有助于计算大模型的算力峰值和算力利用率。 GPU 硬件基础概念GP…