单链表(C语言版本)

news2025/1/9 16:25:53

前提

  1. 不探讨头结点
  2. 空链表可以插入和查找,不可删除
  3. 一般不选择phead移动,定义一个新结点把phead赋给他,移动新结点即可
  4. 单链表不适合在前面和后面插入或删除,适合在后面插入删除

头插

void SLPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuySLTNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

在这里插入图片描述

尾插

//给新结点开辟空间并传入值
SLTNode* BuySLTNode(SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
	}
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}
//不为空的链表的尾插
//尾插的本质是尾结点中要存放新的尾结点的地址

//void SLPushBack(SLTNode* phead, SLTDataType x)
void SLPushBack(SLTNode** pphead, SLTDataType x)
{
	给新结点开辟空间
	//SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	//if (newnode == NULL)
	//{
	//	perror("malloc fail");
	//}
	//newnode->data = x;
	//newnode->next = NULL;

	SLTNode* newnode = BuySLTNode(x);

	if (*pphead == NULL)
	{
		*pphead= newnode;
	}
	else
	{
		//找尾
		//此时不需要用二级指针
		//要改变的是结构体,与前不同,前面要改变的是结构体的指针
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

在这里插入图片描述

尾删

  1. 要判断链表是否为空
  2. 注意剩一个结点和多个结点的删除方法不同
  3. 要找到前一个结点的指针
//第一种方法
void SLPopBack1(SLTNode** pphead)
{
	//链表本身就为空的情况
	// 方法一:
	/*if (*pphead == NULL)
		return;*/
	//方法二:
	
	//顺序反则没有意义
	assert(pphead);
	assert(*pphead);
	
	//注意优先级
	//一个结点
	if ((*pphead)->next == NULL)
	{
		//这里说明,尾删不能用一级指针
		free(*pphead);
		*pphead = NULL;
	}
	//多个结点
	else
	{
		SLTNode* tail = *pphead;
		//定义prev指针指向最后一个结点的前一个结点
		SLTNode* prev = NULL;
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		//不加一个结点的判断,在这里会出问题,不进入循环
		prev->next = NULL;
	}
}
//第二种方法
void SLPopBack2(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	//一个结点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	//多个结点
	else
	{
		SLTNode* tail = *pphead;
		//不加一个结点的判断,在这里会出问题,不进入循环
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

头删

不需要判断一个结点的状况

void SLPopFront(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	SLTNode* first = *pphead;
	*pphead = first->next;
	free(first);
	first = NULL;
}

插入/删除数据

1.在pos指针之前插入

  1. 首先要考虑头插,其次要找到pos的前一个位置
  2. 类似于尾删
//pos结点前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pos);
	assert(pphead);
	//头插
	if (pos == *pphead)
	{
		SLPushFront(pphead, x);
	}
	else
	{
		//找到pos结点的前一个结点
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		//要添加的结点以及值
		SLTNode* newnode = BuySLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

在这里插入图片描述

2. 在pos位置删除

//pos位置删除
void SLErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pos);
	assert(pphead);
	assert(*pphead);

	//头删
	if (pos == *pphead)
	{
		SLPopFront(pos);
	}
	else
	{
		//找到pos结点的前一个结点
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		//滞空无用,形参改变不影响实参
		//pos = NULL;
	}
}

3. pos后面插入

void SLInsertAfter(SLTNode* pos,SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = BuySLTNode(x);
	//顺序不可返
	//否则数据丢失
	newnode->next = pos->next;
	pos->next = newnode;
}

4. pos后面删除

void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);
	assert(pos->next);
	
	//ok
	//SLTNode* del = pos->next;
	//pos->next = pos->next->next;
	//free(del);
	//del = NULL;

	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}

链表的销毁

传地址,不需要手动制空

//链表的销毁
void SLTDestory1(SLTNode* phead)
{
	SLTNode* cur = phead;
	//错误写法,cur和tmp指向一致,free(cur) 等同于free(tmp)
	/*while (cur)
	{
		SLTNode* tmp = cur;
		free(cur);
		cur = tmp->next;
	}*/
	//正确写法
	while (cur)
	{
		SLTNode* tmp = cur->next;
		free(cur);
		cur = tmp;
	}
	//不起作用--形参不改变实参
	//phead = null;
}
void SLTDestory2(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* tmp = cur->next;
		free(cur);
		cur = tmp;
	}
	*pphead = NULL;
}

测试

void TestLinkList1()
{
	SLTNode* plist = NULL;
	//形参的地址不改变实参
	SLPushBack(&plist, 2);
	SLPushBack(&plist, 3);
	SLPushBack(&plist, 4);
	SLPushBack(&plist, 5);
	printf("尾插法:");
	SLTPrint(plist);
	printf("头删法:");
	SLPopFront(&plist);
	SLTPrint(plist);
	SLPopFront(&plist);
	SLTPrint(plist);
	SLPopFront(&plist);
	SLTPrint(plist);
	SLPopFront(&plist);
	SLTPrint(plist);
	//空链表 -- 报错
	/*SLPopFront(&plist);
	SLTPrint(plist);*/
	SLTDestory2(&plist);
}

void TestLinkList2()
{
	SLTNode* plist = NULL;
	SLPushFront(&plist, 1);
	SLPushFront(&plist, 2);
	SLPushFront(&plist, 3);
	SLPushFront(&plist, 4);
	printf("头插法:");
	SLTPrint(plist);
	printf("尾删法:");
	SLPopBack1(&plist);
	SLTPrint(plist);
	SLPopBack2(&plist);
	SLTPrint(plist);
	SLPopBack1(&plist);
	SLTPrint(plist);
	SLPopBack2(&plist);
	SLTPrint(plist);
	//空了再删 -- assert警告
	/*SLPopBack2(&plist);
	SLTPrint(plist);*/

	//销毁 -- 手动制空
	SLTDestory(plist);
	plist = NULL;
}
TestLinkList3()
{
	SLTNode* plist = NULL;
	SLPushFront(&plist, 2);
	SLPushFront(&plist, 5);
	SLPushFront(&plist, 7);
	SLPushFront(&plist, 9);
	
	//值为2的结点的值*2
	printf("查找值为2的结点的值乘2:");
	SLTNode* ret = SLFind(plist, 2);
	ret->data *= 2;
	SLTPrint(plist);

	//pos结点前插入
	//找到结点位置
	SLTNode* ret2 = SLFind(plist, 5);
	printf("在值为%d前插入:",ret2->data);
	SLInsert(&plist,ret2, 100);
	SLTPrint(plist);
	
	//在pos位置删除
	SLTNode* ret3 = SLFind(plist, 7);
	printf("删除值为%d的数:", ret3->data);
	SLErase(&plist,ret3);
	SLTPrint(plist);

	//在pos后插入
	SLTNode* ret4 = SLFind(plist,5 );
	printf("在%d后插入数据:", ret4->data);
	SLInsertAfter(ret4, 299);
	SLTPrint(plist);

	//在pos后删除
	printf("在%d后删除数据:", ret4->data);
	SLTEraseAfter(ret4);
	SLTPrint(plist);

}
int main()
{
	TestLinkList1();
	TestLinkList2();
	TestLinkList3();
	return 0;
}

在这里插入图片描述

补充

区分两种断言,一定不能为空才断言
在这里插入图片描述

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

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

相关文章

VMware虚拟机搭建和镜像配置

VMware虚拟机搭建和镜像配置 下载安装VMware 开始下载 更改安装路径,需要一个大空间的盘 更改后下一步 下一步后,选择不主动升级更新 一直下一步 直到安装完毕 输入许可密钥,我下载的版本是12,输入完成点击输入&#xff…

使用PPT科研绘图导出PDF边缘留白问题解决方案

使用PPT画图导出PDF格式后,边缘有空白,插入latex不美观,解决方案为自定义PPT幻灯片母版大小,如题步骤为: 1、查看已制作好的图片的大小,即长度和宽度 2、选择自定义幻灯片大小 3、自定义幻灯片大小为第1…

在Ubuntu上使用docker compose安装N卡GPU的Ollama服务

在现代计算环境中,利用 GPU 进行计算加速变得越来越重要。下面将讲解如何在Ubuntu上使用docker compose安装N卡GPU的Ollama服务。 1、安装 NVIDIA 容器工具 首先,需要确保你的系统已经安装了 NVIDIA 容器工具 nvidia-container-toolkit。这是让 Docker 容器访问 GPU 的关键…

如何借助前端表格控件实现金融投资分析平台?

最新技术资源(建议收藏) https://www.grapecity.com.cn/resources/ 金融投资分析背景介绍 金融投资分析是金融领域的核心活动,它要求对资产、市场及经济数据进行深入研究,以识别并评估潜在的投资机会与风险。这一过程融合了宏观经…

01_Node.js入门 (黑马)

01_Node.js入门 知识点自测 从 index.js 出发&#xff0c;访问到 student/data.json 的相对路径如何写? A&#xff1a;../public/teacher/data.json B&#xff1a;./public/student/data.json C&#xff1a;../student/data.json <details><summary>答案</sum…

快速构建NLP理论知识体系

NLP理论知识体系 一句话解释NLPNLP模型及原理简述1、Rag 一句话解释NLP 如果我们要实现机器翻译、情感分析、问答系统、文本摘要、聊天机器人、构造智能化的辅助文件填写模板&#xff0c;NLP可以通过现成的模型对输入的语音、文字、图片进行处理&#xff08;分词、标词性、去停…

Python:import语句的详细解析(绝对路径导入和相对路径导入)

相关阅读 Pythonhttps://blog.csdn.net/weixin_45791458/category_12403403.html?spm1001.2014.3001.5482 import语句是Python中一个很重要的机制&#xff0c;允许在一个文件中访问另一个文件的函数、类、变量等&#xff0c;本文就将进行详细介绍。 在具体谈论import语句前&a…

CUDA 计时功能,记录GPU程序/函数耗时,cudaEventCreate,cudaEventRecord,cudaEventElapsedTime

为了测试GPU函数的耗时&#xff0c;可以使用 CUDA 提供的计时功能&#xff1a;cudaEventCreate, cudaEventRecord, 和 cudaEventElapsedTime。这些函数可以帮助你测量某个 CUDA 操作&#xff08;如设置设备&#xff09;所花费的时间。 一、记录耗时案例 以下是一个示例程序&a…

数字图像处理(15):图像平移

&#xff08;1&#xff09;图像平移的基本原理&#xff1a;计算每个像素点的移动向量&#xff0c;并将这些像素按照指定的方向和距离进行移动。 &#xff08;2&#xff09;平移向量包括水平和垂直分量&#xff0c;可以表示为&#xff08;dx&#xff0c;dy&#xff09;&#xff…

Hyper-V安装Win11虚拟机并设置vGPU显卡直通

一、为什么我使用Hyper-V虚拟机 我的宿主机是Win11,想装一个Win10或Win11虚拟机。但是我用VMware安装Win10或Win11后,随机地蓝屏,非常烦人,估计是和宿主机的某些设置有关,或者宿主机电脑硬件比较新(我电脑装Win10就会蓝屏,Win11就不会),某些特性不支持。 所以我就安…

Qt Xlsx安装教程

Qt Xlsx安装教程 安装perl 如果没有安装perl&#xff0c;请参考perl Window安装教程 下载QtXlsxWriter源码 下载地址 ming32-make编译32 lib库 C:\Qt\Qt5.12.12\5.12.12\mingw73_32>d: D:\>cd D:\Code\QtXlsxWriter-master\QtXlsxWriter-master D:\Code\QtXlsxWrit…

C# RSA加密和解密,RSA生成私钥和公钥

C# RSA加密和解密&#xff0c;RSA生成私钥和公钥&#xff08;使用XML格式秘钥&#xff09; 目录 前言生成xml格式的公钥和私钥 PrivateKeyPublicKey测试加密、解密 方案1&#xff1a;RSA公钥加密&#xff0c;RSA私钥解密方案2&#xff1a;RSA私钥加密&#xff0c;RSA私钥解密…

【Rive】Android与Rive交互

1 Android与Rive交互的常用接口 1.1 RiveAnimationView参数 <app.rive.runtime.kotlin.RiveAnimationViewandroid:id"id/rive_view"android:layout_width"match_parent"android:layout_height"match_parent"android:adjustViewBounds"…

捷米特 EtherNet/IP 总线协议网关的具体内容介绍

关于EtherNET/IP的基本介绍 EtherNet/IP 中的 “Ethernet” 指以太网&#xff0c;是一种常见的局域网技术&#xff0c;用于在有限区域内实现多台设备之间的数据传输&#xff1b;“IP” 在此处指工业协议&#xff08;Industrial Protocol&#xff09;&#xff0c;而不是通常所说…

Python 读取 Excel 表格并导出为 DBF 文件

以下是将上述代码封装为函数的版本。函数接收 input_excel_path、sheet_name 和 output_dbf_path 作为参数&#xff0c;按照需求读取 Excel 表格并导出为 DBF 文件。 封装函数代码 import pandas as pd import dbfdef excel_to_dbf(input_excel_path, sheet_name, output_dbf_…

使用 ASP.NET Core HttpLoggingMiddleware 记录 http 请求/响应

我们发布了一个应用程序&#xff0c;该应用程序运行在一个相当隐蔽的 WAF 后面。他们向我们保证&#xff0c;他们的产品不会以任何方式干扰我们的应用程序。这是错误的。他们删除了我们几乎所有的“自定义”标头。为了“证明”这一点&#xff0c;我构建了一个中间件&#xff0c…

EasyExcel改名为FastExce做了那些改变呢

回到&#xff1a;github原作者地址&#xff1a;https://github.com/CodePhiliaX/fastexcel 中文 |English | 什么是 FastExcel FastExcel 是由原 EasyExcel 作者创建的新项目。2023 年我已从阿里离职&#xff0c;近期阿里宣布停止更新 EasyExcel&#xff0c;作者他本人决定继…

TCP/IP协议详解(小白)

TCP/IP协议详解 TCP/IP协议包含了一系列的协议&#xff0c;也叫TCP/IP协议族&#xff08;TCP/IP Protocol Suite&#xff0c;或TCP/IP Protocols&#xff09;&#xff0c;简称TCP/IP。TCP/IP协议族提供了点对点的连结机制&#xff0c;并且将传输数据帧的封装、寻址、传输、路由…

【工具】音频文件格式转换工具

找开源资源、下载测试不同库的效果&#xff0c;然后找音频、下载音频、编写代码、测试转换、流程通畅。写一个工具花的时间越来越多了&#xff01;这个 5 天 这个工具是一个音频文件格式转换工具&#xff0c;支持对 mp3.aac.wav.caf.flac.ircam.mp2.mpeg.oga.opus.pcm.ra.spx.…

Vue智慧商城项目

创建项目 vue组件库 — vant-ui&#xff08;常用于移动端&#xff09; Vant 2 - 轻量、可靠的移动端组件库 安装vant npm i vantlatest-v2 -S 引入组件 按需导入和全部导入 全部导入 整个组件库的所有组件都导进来&#xff0c;缺点是增加了代码包体积 main.js import…