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

news2024/11/18 13:29:05

一.前言

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

二.正文

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.coloradmin.cn/o/1637229.html

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

相关文章

“云卷数潮”云原生数据库分论坛亮点回顾!

4月29日&#xff0c;2024中国移动算力网络大会“云卷数潮”云原生数据库分论坛在江苏苏州举行。本次论坛不仅是技术交流的盛宴&#xff0c;更是行业发展趋势的风向标。论坛汇聚了众多企业领袖、专家学者及行业精英&#xff0c;共话云原生数据库技术发展&#xff0c;探讨行业最新…

无人机+大载重+长航时:油电混动多旋翼无人机技术详解

多旋翼无人机是一种具有三个及以上旋翼轴的特殊的无人驾驶旋翼飞行器。具有稳定性强、操控简单、勤务性高、价格便宜等优势&#xff0c;因此在市场上的应用非常广泛。此外&#xff0c;利用地面供电的绳系多旋翼通过电缆向多旋翼持续传输电能&#xff0c;可以大大提高多旋翼的空…

QT程序通过GPIB-USB-HS转接线控制数字万用表

1、硬件准备 1.1、数字万用表 型号 &#xff1a;Agilent 34401A 前面图示&#xff1a; 后面图示&#xff1a;有GPIB接口 1.2、GPIB-USB-HS转接线 2、GPIB协议基础了解 2.1、引脚 8条数据线&#xff1a;DIO1 ~ DIO8 5条管理线&#xff1a;IFC、ATN、REN、EOI、SRQ 3条交握线…

2024年五一杯高校数学建模竞赛(C题) 建模解析| 冲击地压危险预测 |小鹿学长带队指引全代码文章与思路

我是鹿鹿学长&#xff0c;就读于上海交通大学&#xff0c;截至目前已经帮200人完成了建模与思路的构建的处理了&#xff5e; 本篇文章是鹿鹿学长经过深度思考&#xff0c;独辟蹊径&#xff0c;通过滑动平均法解决冲击地压危险预测问题。实现综合建模。独创复杂系统视角&#xf…

Android滑动冲突解决方案面试问题及回答

问题 1: 什么是滑动冲突&#xff1f;在Android开发中常见哪些类型&#xff1f; 答案: 滑动冲突通常发生在多个滑动组件&#xff08;如ScrollView, ListView, ViewPager等&#xff09;嵌套使用时&#xff0c;各个组件对同一个滑动事件的响应发生冲突。常见的类型包括垂直滑动冲…

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

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

算法:双指针题目练习

目录 题目一&#xff1a;移动零 题目二&#xff1a;复写零 题目三&#xff1a;快乐数 题目四&#xff1a;盛最多水的容器 题目五&#xff1a;有效三角形的个数 题目六&#xff1a;和为s的两个数字(剑指offer) 题目七&#xff1a;三数之和 题目八&#xff1a;四数之和 常…

翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习一

合集 ChatGPT 通过图形化的方式来理解 Transformer 架构 翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习一翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习二翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深…

CAD的DWG文件如何进行搜索文字

1.目的 想搜索CAD文件中的数字或文字是否存在DWG文件中。 2.方法 方式1:菜单栏 编辑→查找 方式2&#xff1a;指令格式 图纸的左下侧→命令处&#xff0c;进行输入find→再按回车enter 3.结果

Day 22 SSH远程管理服务

sshd服务&#xff0c;系统自带&#xff0c;默认开机自启运行 云/物理服务器的安全组和防火墙默认放行该端口 软件包&#xff1a;openssh-server&#xff08;服务端&#xff09;&#xff1b;openssh-client&#xff08;客户端&#xff09;&#xff1b; 格式&#xff1a;ssh I…

【C++语法练习】计算梯形的面积

题目链接&#xff1a;https://www.starrycoding.com/problem/158 题目描述 已知一个梯形的上底 a a a&#xff0c;下底 b b b和高 h h h&#xff0c;请求出它的面积&#xff08;结果保留两位小数&#xff09;。 输入格式 第一行一个整数 T T T表示测试用例个数。 ( 1 ≤ T …

JeeSite主子表

classroom表代码生成配置&#xff08;主表&#xff09; student表代码生成配置&#xff08;子表&#xff09; 设置“班级”菜单 班级列表页面 点击“三年级二班”&#xff0c;显示班级中的学生列表页面 班级表 CREATE TABLE classroom (classroom_id int NOT NULL AUTO_…

Meditron:基于 Llama 完全开源的医学大语言模型

健康危机就在眼前&#xff0c;当医疗资源有限时&#xff0c;每一秒钟都至关重要&#xff01;Meditron 就像一位忠实的医疗助手&#xff0c;提供基于证据的护理建议和情境意识的推荐&#xff0c;帮助医疗工作者在诊断和治疗过程中做出更准确的决策。 在资源有限的医疗环境中&am…

C/C++实现高性能并行计算——1.pthreads并行编程(中)

系列文章目录 pthreads并行编程(上)pthreads并行编程(中)pthreads并行编程(下)使用OpenMP进行共享内存编程 文章目录 系列文章目录前言一、临界区1.1 pi值估计的例子1.2 找到问题竞争条件临界区 二、忙等待三、互斥量3.1 定义和初始化互斥锁3.2 销毁。3.3 获得临界区的访问权&…

《读懂财务报表》手绘版读书笔记:通过报表找好公司

通过财报的三张表判断好公司&#xff1a; 然后是在三表中&#xff0c;计算各个项目占总体的比例&#xff0c;以及做比率分析&#xff0c; 比率分析&#xff0c;从偿还能力&#xff0c;运营能力&#xff0c;盈利能力三方面分析&#xff1a; 1&#xff09; 偿还能力 2&#xff09…

新建stm32工程模板步骤

1.先使用keil新建一个project的基本代码 2.stm32启动文件添加 将stm32的启动文件&#xff0c;在原工程当中新建一个Start文件夹把相关的启动文件放到文件夹当中 然后还需要找到下面三个文件 stm32f10x.h是stm32的外设寄存器的声明和定义&#xff0c;后面那两个文件用于配置系…

Python数据预处理1:导入与基本操作

2024/4/30 After installing the xlrd package, you should be able to read Excel files using pandas without any issues. #需要在pyCharm命令行中下载两个包 pip install pandas pip install xlrd .xls数据导入 #数据的导入 import pandas as pd #导入EXCEL表格数据 df…

Java基于微信小程序+uniapp的校园失物招领小程序(V3.0)

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

【大数据】利用 Apache Ranger 管理 Amazon EMR 中的数据权限

利用 Apache Ranger 管理 Amazon EMR 中的数据权限 1.需求背景简介2.系统方案架构图3.主要服务和组件简介3.1 Amazon EMR3.2 Simple Active Directory3.3 Apache Ranger 4.部署步骤4.1 部署 Simple AD 服务4.2 部署 Apache Ranger4.3 部署 Amazon EMR4.4 在 Amazon EMR 的主节点…

FPGA高端项目:FPGA帧差算法多目标图像识别+目标跟踪,提供11套工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐FPGA帧差算法单个目标图像识别目标跟踪 3、详细设计方案设计原理框图运动目标检测原理OV5640摄像头配置与采集OV7725摄像头配置与采集RGB视频流转AXI4-StreamVDMA图像缓存多目标帧差算法图像识别目标跟踪模块视频输出Xilinx系列FPGA工程源…