数据结构-->线性表-->单链表

 

链表的定义

链表:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

与顺序表不同的是,链表里的每节都是独立申请下来的空间,我们称之为“节点、结点”。

节点的组成主要由两个部分:当前节点要保存的数据和保存下一个节点的地址(指针变量)。

链表的节点(结点)

链表中的每个节点都是独立申请的(需要插入数据时才去申请一块节点的空间),我们需要通过指针变量来保存下一个节点位置才能从当前节点找到下一个节点。

给出每个节点对应的结构体代码:以保存的节点是整形为例:

struct SListNode
{
 int data; //节点数据
 struct SListNode* next; //指针变量⽤保存下⼀个节点的地址
};

当我们想要保存一个整型数据时,实际是向操作系统申请了一块内存,这个内存不仅要保存整型数据,也需要保存下一个节点的地址(当下一个节点为空时保存的地址为空)。

链式结构在逻辑上是连续的,在物理结构上不一定连续

节点一般是从堆上申请的

从堆上申请来的空间,是按照一定策略分配出来的,每次申请的空间可能连续,可能不连续。

链表的分类 

对于链表的分类来说:一共有8种

最常用的两种链表结构 

虽然链表结构很多,但最常用的只有两种结构
单链表:也叫无头单向非循环链表,结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构。
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。这个链表虽然结构复杂,但是使用代码实现以后会发现法结构会带来更多优势。

 

首先我们给出我们要实现的单链表的头文件:

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLDataType;
typedef struct SList
{
	SLDataType data;
	struct SList* next;
}SL;
//打印
void SLPrint(SL* phead);
//销毁
void SLDestroy(SL** pphead);
//开辟新链表节点
SL* Buynode(SLDataType x);
//链表头插
void SLPushFront(SL** pphead,SLDataType x);
//链表尾插
void SLPushBack(SL** pphead,SLDataType x);
//链表头删
void SLPopFront(SL** pphead); 
//链表尾删
void SLPopBack(SL** pphead);
//链表对节点的查找
SL* SLFind(SL** pphead,SLDataType x);
//对指定位置之前插入数据
void SLInsert(SL** pphead,SL* pos,SLDataType x);
//在指定位置之后插入数据
void SLInsertafter(SL* pos, SLDataType x);
//删除pos节点
void SLErase(SL** pphead,SL* pos);
//删除pos之后的节点
void SLEraseafter(SL* pos);


对单链表打印


这里直接进行遍历就ok

//打印
void SLPrint(SL* phead)
{
	SL* pcur = phead;
	while (pcur)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL");
}

销毁单链表 

逐个销毁,销毁某一个节点时,保存他的下一个节点的地址。

//销毁
void SLDestroy(SL** pphead)
{
	assert(pphead);
	assert(*pphead);

	SL* pcur = *pphead;
	while (pcur)
	{
		SL* next = (*pphead)->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

 开辟节点空间

为新的数据开辟空间

//开辟节点空间
SL* Buynode(SLDataType x)
{
	SL* newnode = (SL*)malloc(sizeof(SL));
	if (newnode == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	
	return newnode;
}

 单链表头插

记得先后顺序,个人感觉容易犯错

//链表头插
void SLPushFront(SL** pphead, SLDataType x)
{
	assert(pphead);
	SL* new = Buynode(x);
	//个人觉得易错
	new->next = *pphead;
	*pphead = new;
}

单链表尾插

如果头结点为空,则相当于头插

如果头结点不为空,就正常找尾节点然后插入。

//链表尾插
void SLPushBack(SL** pphead, SLDataType x)
{
	assert(pphead);
	SL* new = Buynode(x);
	if (*pphead == NULL)
	{
		*pphead = new;
		return;
	}
	SL* pcur = *pphead;
	while (pcur->next)
	{
		pcur = pcur->next;
	}
	pcur->next = new;
}

 单链表头删

 对于删除来说,需要断言pphead和*pphead,pphead是存放*pphead的地址不能为NULL,

*pphead是为了判断单链表不能为NULL

//链表头删
void SLPopFront(SL** pphead)
{
	assert(pphead);
	assert(*pphead);

	SL* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

 单链表尾删

如果只有一个元素,就相当于头删,否则就相当于尾删

//链表尾删
void SLPopBack(SL** pphead)
{
	assert(pphead);
	assert(*pphead);
	if (((*pphead)->next) == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	SL* prev = NULL;
	SL* pcur = *pphead;
	while (pcur->next)
	{
		prev = pcur;
		pcur = pcur->next;
	}
	free(pcur);
	pcur = NULL;
	prev->next = NULL;
}

单链表对节点的查找

遍历查找与目标值相同的,然后返回存储该值的地址

//链表对节点的查找
SL* SLFind(SL** pphead, SLDataType x)
{
	assert(pphead);
	SL* pcur = *pphead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

对指定位置插入数据

1.pos在*pphead,相当于头插

2.pos不在*pphead,就正常插入

//对指定位置之前插入数据
void SLInsert(SL** pphead, SL* pos, SLDataType x)
{
	assert(pphead);
	assert(pos);
	assert(*pphead);
	SL* new = Buynode(x);
	if (pos == *pphead)
	{
		SLPushFront(pphead, x);
		return;
	}
	SL* pcur = *pphead;
	while (pcur->next!=pos)
	{
		pcur = pcur->next;
	}
	pcur->next = new;
	new->next = pos;
}

 在指定位置后插入

没什么好说的,直接找到指定位置,然后插入即可

//在指定位置之后插入数据
void SLInsertafter(SL* pos, SLDataType x)
{
	assert(pos);
	SL* new = Buynode(x);
	new->next = pos->next;
	pos->next = new;
}

 删除pos节点的值

如果pos为*pphead就头删,否则就正常删除pos节点的值

//删除pos节点
void SLErase(SL** pphead, SL* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);
	if (*pphead == pos)
	{
		SLPopFront(pphead);
		return;
	}
	SL* pcur = *pphead;
	while (pcur->next != pos)
	{
		pcur = pcur->next;
	}
	SL* next = pos->next;
	pcur->next = next;
	free(pos);
	pos=NULL;
}

 删除pos节点之后的值

找到pos节点,当然联系pos和pos->next->next的值

//删除pos之后的节点
void SLEraseafter(SL* pos)
{
	assert(pos);
	assert(pos->next);
	SL* pcur = pos->next;
	pos->next = pos->next->next;
	free(pcur);
	pcur = NULL;
}

 最终代码

SList.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLDataType;
typedef struct SList
{
	SLDataType data;
	struct SList* next;
}SL;
//打印
void SLPrint(SL* phead);
//销毁
void SLDestroy(SL** pphead);
//开辟新链表节点
SL* Buynode(SLDataType x);
//链表头插
void SLPushFront(SL** pphead,SLDataType x);
//链表尾插
void SLPushBack(SL** pphead,SLDataType x);
//链表头删
void SLPopFront(SL** pphead); 
//链表尾删
void SLPopBack(SL** pphead);
//链表对节点的查找
SL* SLFind(SL** pphead,SLDataType x);
//对指定位置之前插入数据
void SLInsert(SL** pphead,SL* pos,SLDataType x);
//在指定位置之后插入数据
void SLInsertafter(SL* pos, SLDataType x);
//删除pos节点
void SLErase(SL** pphead,SL* pos);
//删除pos之后的节点
void SLEraseafter(SL* pos);

SList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"
//打印
void SLPrint(SL* phead)
{
	SL* pcur = phead;
	while (pcur)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL");
}
//销毁
void SLDestroy(SL** pphead)
{
	assert(pphead);
	assert(*pphead);

	SL* pcur = *pphead;
	while (pcur)
	{
		SL* next = (*pphead)->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}
//开辟节点空间
SL* Buynode(SLDataType x)
{
	SL* newnode = (SL*)malloc(sizeof(SL));
	if (newnode == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	
	return newnode;
}
//链表头插
void SLPushFront(SL** pphead, SLDataType x)
{
	assert(pphead);
	SL* new = Buynode(x);
	//个人觉得易错
	new->next = *pphead;
	*pphead = new;
}
//链表尾插
void SLPushBack(SL** pphead, SLDataType x)
{
	assert(pphead);
	SL* new = Buynode(x);
	if (*pphead == NULL)
	{
		*pphead = new;
		return;
	}
	SL* pcur = *pphead;
	while (pcur->next)
	{
		pcur = pcur->next;
	}
	pcur->next = new;
}
//链表头删
void SLPopFront(SL** pphead)
{
	assert(pphead);
	assert(*pphead);

	SL* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}
//链表尾删
void SLPopBack(SL** pphead)
{
	assert(pphead);
	assert(*pphead);
	if (((*pphead)->next) == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	SL* prev = NULL;
	SL* pcur = *pphead;
	while (pcur->next)
	{
		prev = pcur;
		pcur = pcur->next;
	}
	free(pcur);
	pcur = NULL;
	prev->next = NULL;
}
//链表对节点的查找
SL* SLFind(SL** pphead, SLDataType x)
{
	assert(pphead);
	SL* pcur = *pphead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}
//对指定位置之前插入数据
void SLInsert(SL** pphead, SL* pos, SLDataType x)
{
	assert(pphead);
	assert(pos);
	assert(*pphead);
	SL* new = Buynode(x);
	if (pos == *pphead)
	{
		SLPushFront(pphead, x);
		return;
	}
	SL* pcur = *pphead;
	while (pcur->next!=pos)
	{
		pcur = pcur->next;
	}
	pcur->next = new;
	new->next = pos;
}
//在指定位置之后插入数据
void SLInsertafter(SL* pos, SLDataType x)
{
	assert(pos);
	SL* new = Buynode(x);
	new->next = pos->next;
	pos->next = new;
}
//删除pos节点
void SLErase(SL** pphead, SL* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);
	if (*pphead == pos)
	{
		SLPopFront(pphead);
		return;
	}
	SL* pcur = *pphead;
	while (pcur->next != pos)
	{
		pcur = pcur->next;
	}
	SL* next = pos->next;
	pcur->next = next;
	free(pos);
	pos=NULL;
}
//删除pos之后的节点
void SLEraseafter(SL* pos)
{
	assert(pos);
	assert(pos->next);
	SL* pcur = pos->next;
	pos->next = pos->next->next;
	free(pcur);
	pcur = NULL;
}

Test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"
int main()
{
	SL* plist = NULL;
	SLPushBack(&plist, 1);
	SLPushBack(&plist, 2);//1->2
	//SLPrint(plist);
	SLPushFront(&plist, 3);//3->1->2
	//SLPrint(plist);
	SLPushFront(&plist, 4);
	//SLPrint(plist);//4->3->1->2
	SLPopBack(&plist);//4->3->1
	//SLPrint(plist);
	SLPopFront(&plist);//3->1
	//SLPrint(plist);
	SL* new=SLFind(&plist, 3);
	SLInsert(&plist,new,4);//4->3->1
	//SLPrint(plist);
	SLInsertafter(new, 5);//4->3->5->1
	//SLPrint(plist);
	SLErase(&plist, new);//4->5->1
	SLPrint(plist);
	SLDestroy(&plist);

	return 0;
}

运行test函数:

以及测试过了,每个接口函数都没啥问题

 

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

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

相关文章

横扫Spark之 - 22个常见的转换算子

水善利万物而不争&#xff0c;处众人之所恶&#xff0c;故几于道&#x1f4a6; 文章目录 1. map()2. flatMap()3. filter()4. mapPartitions()5. mapPartitionsWithIndex()6. groupBy()7. distinct()8. coalesce()9. repartition()10. sortBy()11. intersection()12.union()13.…

5G技术对物联网的影响

随着数字化转型的加速&#xff0c;5G技术作为通信领域的一次重大革新&#xff0c;正在对物联网&#xff08;IoT&#xff09;产生深远的影响。对于刚入行的朋友们来说&#xff0c;理解5G技术及其对物联网应用的意义&#xff0c;是把握行业发展趋势的关键。 让我们简单了解什么是…

使用python-numpy实现一个简单神经网络

目录 前言 导入numpy并初始化数据和激活函数 初始化学习率和模型参数 迭代更新模型参数&#xff08;权重&#xff09; 小彩蛋 前言 这篇文章&#xff0c;小编带大家使用python-numpy实现一个简单的三层神经网络&#xff0c;不使用pytorch等深度学习框架&#xff0c;来理解…

探索设计模式的魅力:代理模式揭秘-软件世界的“幕后黑手”

设计模式专栏&#xff1a;http://t.csdnimg.cn/U54zu 目录 引言 一、魔法世界 1.1 定义与核心思想 1.2 静态代理 1.3 动态代理 1.4 虚拟代理 1.5 代理模式结构图 1.6 实例展示如何工作&#xff08;场景案例&#xff09; 不使用模式实现 有何问题 使用模式重构示例 二、…

【Rust日报】2024-02-08 Loungy:使用 Rust 和 GPUI 开发的 MacOS 启动器

Mira Screenshare&#xff1a;基于 Rust 和 WebRTC 的高性能屏幕分享工具 一群大学生宣布推出了他们的期末项目&#xff1a;Mira Screenshare&#xff0c;一个开源、高性能的屏幕共享工具&#xff0c;由 Rust 和 WebRTC 构建。此项目支持 4k 60 FPS 和 110ms 端到端延迟的屏幕…

CS50x 2024 - Lecture 2 - Arrays

00:00:00 - Introduction 00:01:01 - Story Time 00:06:03 - Compiling make本身并不是编译器&#xff0c;实际上是一个自动运行编译器的程序&#xff0c;如c语言的clang clang -o hello hello.csrc/ $ clang -o hello hello_world.c /usr/bin/ld: /tmp/hello_world-67f51…

Oracle 面试题 | 19.精选Oracle高频面试题

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

你的立身之本是什么?

去年发生的一切&#xff0c;大到疫情、政治经济形势、行业的萎靡和震荡&#xff0c;小到身边的跳槽、裁员、公司倒闭……似乎都在告诉我们&#xff1a; 当冲击到来的时候&#xff0c;它是不会提前跟你打招呼的。 接下来的10年&#xff0c;我们所面临的不确定性&#xff0c;比起…

Linux 软件管理(YUM RPM)

1 YUM yum&#xff08;全称为 Yellow dog Updater, Modified&#xff09;是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器。基于RPM包管理&#xff0c;能够从指定的服务器自动处理依赖性关系&#xff0c;并且一次安装所有依赖的软件包&#xff0c;无须繁琐地一次次…

fps cf游戏,一键断网辅助工具

一键断网瞬移 工具特色&#xff1a;一改常规断网操作&#xff08;断网开启&#xff0c;所有人都卡住&#xff0c;使得还原后找不到人的问题 &#xff09;&#xff0c;不影响任何人移动&#xff0c;开启断网跟着别人一起走&#xff0c;其他人无任何异常卡顿。 工具功能&…

Linux应用程序几种参数传递方式

大家好&#xff0c;今天给大家介绍Linux应用程序几种参数传递方式&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 在Linux中&#xff0c;应用程序可以通过多种方式接收参数。以下…

文心一言 VS 讯飞星火 VS chatgpt (198)-- 算法导论14.3 6题

六、用go语言&#xff0c;说明如何来维护一个支持操作MIN-GAP的一些数的动态集Q&#xff0c;使得该操作能给出Q中两个最接近的数之间的差值。例如&#xff0c;Q(1&#xff0c;5&#xff0c;9&#xff0c;15&#xff0c;18&#xff0c;22)&#xff0c;则MIN-GAP返回18-153&#…

【EAI 011】SayCan: Grounding Language in Robotic Affordances

论文标题&#xff1a;Do As I Can, Not As I Say: Grounding Language in Robotic Affordances 论文作者&#xff1a;Michael Ahn, Anthony Brohan, Noah Brown, Yevgen Chebotar, Omar Cortes, Byron David, Chelsea Finn, Chuyuan Fu, Keerthana Gopalakrishnan, Karol Hausm…

【正式】今年第一篇CSDN(纯技术教学)

一、文件上传简介 文件上传漏洞是指用户上传了一个可执行的脚本文件&#xff08;木马、病毒、恶意脚本、webshell等&#xff09;&#xff0c;并通过此脚本文件获得了执行服务器端命令的能力。上传点一般出现在头像、导入数据、上传压缩包等地方&#xff0c;由于程序对用户上传…

C语言笔试题之求出二叉树的最大深度(递归解决)

实例要求&#xff1a; 1、给定一个二叉树 root &#xff0c;返回其最大深度&#xff1b;2、二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数&#xff1b; 案例展示&#xff1a; 实例分析&#xff1a; 1、判断根节点是否为空&#xff1b;2、分别递归处理左…

物联网数据隐私保护技术

在物联网&#xff08;IoT&#xff09;的世界中&#xff0c;无数的设备通过互联网连接在一起&#xff0c;不断地收集、传输和处理数据。这些数据有助于提高生产效率、优化用户体验并创造新的服务模式。然而&#xff0c;随着数据量的剧增&#xff0c;数据隐私保护成为了一个不能忽…

【java苍穹外卖项目实战二】苍穹外卖环境搭建

文章目录 1、前端环境搭建2、后端环境搭建1、项目结构搭建2、Git版本控制3、数据库创建 开发环境搭建主要包含前端环境和后端环境两部分。 前端的页面我们只需要导入资料中的nginx&#xff0c; 前端页面的代码我们只需要能看懂即可。 1、前端环境搭建 前端运行环境的nginx&am…

《MySQL 简易速速上手小册》第7章:MySQL监控和日志分析(2024 最新版)

文章目录 7.1 配置和使用 MySQL 监控工具7.1.1 基础知识7.1.2 重点案例&#xff1a;使用 Python 和 Prometheus 监控 MySQL 性能7.1.3 拓展案例 1&#xff1a;自动化 MySQL 慢查询日志分析7.1.4 拓展案例 2&#xff1a;实时警报系统 7.2 解读 MySQL 日志文件7.2.1 基础知识7.2.…

【Spring】Bean 的实例化方式

Spring 为 Bean 提供了多种实例化方式&#xff0c;通常包括4种方式 也就是说在 Spring 中为 Bean 对象的创建准备了多种方案&#xff0c;目的是&#xff1a;更加灵活 第一种&#xff1a;通过构造方法实例化 第二种&#xff1a;通过简单工厂模式实例化 第三种&#xff1a;通过…

【第二届 Runway短视频创作大赛】——截至日期2024年03月01日

短视频创作大赛 关于AI Fil&#xff4d; Festival竞赛概况参加资格报名期间报名方法 提交要求奖品附录 关于AI Fil&#xff4d; Festival 2022年成立的AIFF是一个融合了最新AI技术于电影制作中的艺术和艺术家节日&#xff0c;让我们得以一窥新创意时代的风采。从众多参赛作品中…