数据结构——单链表

news2024/10/6 4:02:57

一.简介

上一篇文章,我们介绍了线性表中的顺序表。

顺序表拥有一些缺陷

1.空间不够时需要增容,增容需要付出代价

2.为避免重复扩容,我们进行指数扩容,可能会造成空间浪费

3.顺序表从开始位置连续存储,插入删除数据需要移动,效率不高

而这篇文章,我们来介绍一下链表中的单链表

链表

概念

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

结构

当存储数据时,我们需要在堆上申请空间,因而每个数据都会有相应的地址。

而我们将这个地址存储在一个指针变量中与上一个数据放在一起,便可以将众多数据链接在一起。

 

分类

链表拥有多种结构,可以分为三类

单向/双向

 

带头/不带头 

 head:哨兵位

循环/非循环

 


虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:

无头单向非循环链表

有头双向循环链表

1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结 构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向 循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。

而我们要介绍的便是无头单项非循环链表



二.结构体与初始化

我们将数据与指针变量作为结构成员构建一个结构体,并像顺序表一样进行类型的重命名

typedef int SLTDateType;

typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SLTNode;

不同于结构体,单链表是一种非常简单的结构,因此不需要进行初始化,只需要设立一个结构体空指针。

SLTNode* plist = NULL;


三.功能实现

 

1.打印

显然,我们应该使用循环来进行打印,每次打印完该结构体的data变量时,通过next指针变量前往下一个结构体。而循环的终止条件(即链表走到尾部),应当是结构体的next指针变量为空指针。

void SListPrint(SLTNode* phead)
{
	SLTNode* tail = phead;
	while (tail != NULL)
	{
		printf("%d->", tail->data);
		tail = tail->next;
	}
	printf("NULL\n");
}

2.销毁

void SListDestory(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* cur = *pphead;
	while (cur != NULL)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

3.尾插

不同于顺序表,我们无须扩容,而是每次插入时开辟一块空间

要进行尾插,需要找到链表中尾部的结构体(tail),将新的结构体的地址赋给next变量。

(注意:由于要对链表进行改变,我们要进行传址调用,由于phead本身就是指针,所以我们要用 到二级指针进行传参)

void SListPushBack(SLTNode** pphead, SLTDateType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;

	if (*pphead == NULL)//由于无哨兵位,若链表为空只需要赋值
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
        //找尾
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
	SListPrint(*pphead);
}

4.头插

 当我们进行头插时,需要将phead作为指针变量存储在新创建的结构体的next中,之后将新创建的结构体的地址作为新的phead

void SListPushFront(SLTNode** pphead, SLTDateType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		exit(-1);
	}
	newnode->data = x;
	newnode->next = *pphead;//当链表中没有数据时,为NULL,同样成立
	*pphead = newnode;
	SListPrint(*pphead);
}

5.尾删

当链表中只有一个数据时,我们只需要将其至为空指针

而当拥有多个数据时

我们不止是需要释放尾部的结构体(tail),还需要找到尾部的结构体的前一个(tailPrev),将其中的next置为NULL。 

void SListPopBack(SLTNode** pphead)
{
	assert(*pphead != NULL);
	if ((*pphead)->next != NULL)
	{
		SLTNode* tailPrev = *pphead;
		while (tailPrev->next->next != NULL)
		{
			tailPrev = tailPrev->next;
		}
		free(tailPrev->next);
		tailPrev->next = NULL;
	}
	else
	{
		*pphead = NULL;
	}
	SListPrint(*pphead);
}

6.头删

 若我们先将第二个结构体的地址赋给phead,会找不到第一个结构体,无法进行释放

而若我们先进行释放,会无法找到第二个结构体的地址并将其赋给phead

因此我们需要一个中间变量next

void SListPopFront(SLTNode** pphead)
{
	assert(*pphead != NULL);
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
	SListPrint(*pphead);
}

7.查找数据地址

主要是与插入删除配合使用,没啥好说的,自己看吧。

SLTNode* SListFind(SLTNode* phead, SLTDateType x)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}

8.pos地址前后的插入

后插

void SListInsertAfter(SLTNode* pos, SLTDateType x)
{
	assert(pos);

	SLTNode* newnode = BuyListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

前插

void SListInsertBefore1(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		exit(-1);
	}
	newnode->data = x;
	if (*pphead == pos)
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
	else
	{
		SLTNode* posPrev = *pphead;
		while (posPrev->next != NULL)
		{
			if (posPrev->next==pos)
			{
				newnode->next = pos;
				posPrev->next = newnode;
				break;
			}
			posPrev = posPrev->next;
		}
	}
	SListPrint(*pphead);
}

9.删除

void SListErase(SLTNode** pphead, SLTNode* pos)
{
	assert(*pphead != NULL);
	if (pos == *pphead)
	{
		SLTNode* next = (*pphead)->next;
		free(*pphead);
		*pphead = next;
	}
	else
	{
		SLTNode* posPrev = *pphead;
		while (posPrev->next!= pos)
		{
			posPrev = posPrev->next;
		}
		posPrev->next = pos->next;
		free(pos);
	}
	SListPrint(*pphead);
}


四.优缺点

优点

1.按需申请空间,不用了就释放空间,更合理的使用空间

2.插入删除数据不需要进行挪动

3.不存在空间浪费

缺点

1.每次存一个数据,都要存一个指针链接下一个数据

2.不支持随机访问

(二分查找、优化的快排都需要随机访问)

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

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

相关文章

卡特尔世界杯来了,只喝精酿啤酒不玩望京扑克,其实也是一种缺失

北京时间2022年11月20日,卡特尔世界杯正式拉开了序幕,全球都进入了世界杯时间。世界杯的开幕,最高兴的还是球迷朋友,大家可以欢聚一堂,喝着精酿啤酒看着足球,那滋味别提多舒服了。 世界杯对于广大球迷来说&…

表的增删查改

目录 插入数据 基本查询 更新数据 清空数据 聚合函数 group by子句 内置函数 基本查询练习 多表查询 子查询 合并查询 表的内外连接 插入数据 单行—全列插入 如下图,全列插入可以省略要在哪些列插入! 多行—指定列插入 如下图&#xff0…

安装 Red Hat Enterprise Linux 9.1 虚拟机

目录1. 官方下载链接与新闻2. 安装提示3. 系统安装步骤(1)进入系统引导界面(2)进入【系统语言选择】界面(3)进入【安装信息摘要】界面① 设置【root密码】② 设置【安装目的地】(4)进…

【python】使用python将多个视频合并、延长视频的时间

今天做知识分享的时候,最后保存的视频时长是58分钟,那么如何改变视频的时长,将视频时长改为一个小时呢? 下面提供3个方案: 方案1,重新录,很显然,不合理; 方案2&#xf…

蓝屏page_fault_in_nonpaged_area的解决办法

用户在操作电脑的过程中,难免会遇到蓝屏问题,最近就有用户遇到电脑蓝屏重启无限循环,提示代码page_fault_in_nonpaged_area,这要如何解决呢?下面就来看看详细的解决办法。 page_fault_in_nonpaged_area蓝屏代码解决方法…

【MySQL篇】第一篇——数据库基础

目录 什么是数据库 主流数据库 基本使用 MySQL安装 连接服务器 服务器管理 服务器,数据库,表关系 使用案例 创建数据库 使用数据库 创建数据库表 表中插入数据 查询表中的数据 数据逻辑存储 MySQL架构原理 MySQL整体逻辑架构 MySQL逻辑…

Eureka架构篇 - 服务发现

前言 从客户端与服务端两个角度概述一下Eureka服务发现的原理,如下: 客户端 依赖自动装配机制,客户端启动时就会从Eureka服务端全量获取服务实例的注册信息并缓存到本地。之后每隔30秒向Eureka服务端发起增量获取的请求,如果增…

云原生周刊 | 波音公司允许员工给开源项目做贡献

如果你要问谁对开源项目的贡献最小,那一定是保密等级很高的国防工业机构,但这个魔咒最近被波音公司给打破了。在最近的一次 Linux 基金会成员峰会 keynote 演讲中,波音公司提到他们会在 2022 年成立一个开源办公室,并且从即日起&a…

m基于MATLAB-GUI的GPS数据经纬度高度解析与kalman分析软件设计

目录 1.算法概述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法概述 经度纬度和高度来自GPS信号的中的GPGGA的数据。所以提取这三个信息主要是对GPGGA中的数据进行整理。GPGGA的数据格式如下所示: GPGGA是GPS数据输出格式语句,意思是…

9问502

一、502意味着什么 502 Bad Gateway是指错误网关,无效网关;在互联网中表示一种网络错误。表现在WEB浏览器中给出的页面反馈。它通常并不意味着上游服务器已关闭(无响应网关/代理) ,而是上游服务器和网关/代理使用不一…

latex 模板使用技巧——参考文献篇

参考文献说明: 一、 常用参考文献类型 1、会议 (INPROCEEDINGS) 示例: INPROCEEDINGS{rcnn,title{Rich feature hierarchies for accurate object detection and semantic segmentation},author{Girshick, Ross and Donahue, J…

骨传导耳机是利用什么原理听歌?什么骨传导耳机好用

这几年来骨传导耳机的火热程度不言而喻,很多运动人士手上必有一款骨传导耳机,也随着骨传导耳机的兴起,越来越多小伙伴都加入了运动当中。当然,也有很多小伙伴是不知道骨传导耳机的,更不知道骨传导耳机有什么作用&#…

KESION(.NET版)安装方法

若是windows2008系统,访问xxxxxx.com - xxx sex videos free hd porn 资源和信息。 安装界面没有样式加载,请先编辑web.config 去掉 <defaultDocument> <files> <clear /> <add value"index.aspx" /> </files> </defaultDocume…

Android App开发手机阅读中PDF文件渲染器的讲解及使用(附源码 简单易懂)

需要源码和图片集请点赞关注收藏后评论区留言~~~ 一、PDF文件渲染器 Android集成了PDF的渲染操作&#xff0c;从很大程度上方便了开发者&#xff0c;这个PDF文件渲染器便是PdfRenderer。渲染器允许从存储卡读取PDF文件 打开PDF文件只是第一步&#xff0c;接下来使用PdfRender…

Java FreeMarker模板引擎注入深入分析

0x01 前言 最近和 F1or 大师傅一起挖洞的时候发现一处某 CMS SSTI 的 0day&#xff0c;之前自己在复现 jpress 的一些漏洞的时候也发现了 SSTI 这个洞杀伤力之大。今天来好好系统学习一手。 有三个最重要的模板&#xff0c;其实模板引擎本质上的原理差不多&#xff0c;因为在…

CPT-MNPS/Fe3O4 NPs/Au NPs顺铂偶联磁性纳米粒子/四氧化三铁纳米粒子/金纳米粒子

小编下面整理了CPT-MNPS/Fe3O4 NPs/Au NPs顺铂偶联磁性纳米粒子/四氧化三铁纳米粒子/金纳米粒子&#xff0c;来看&#xff01; CPT-偶联纳米粒子 采用新工艺制备了包载盐酸阿霉素的明胶-泊洛沙姆纳米脂质体&#xff0c;并进行相关性能的表征。采用WW型明胶-泊洛沙姆乳液体系结合…

131. 分割回文串-思路整理

题目 给你一个字符串s&#xff0c;请你将 s分割成一些子串&#xff0c;使每个子串都是回文串。返回 s 所有可能的分割方案。 回文串: 是正着读和反着读都一样的字符串。 输入&#xff1a;s "aab" 输出&#xff1a;[["a","a","b"]…

Advances in Graph Neural Networks笔记4:Heterogeneous Graph Neural Networks

诸神缄默不语-个人CSDN博文目录 本书网址&#xff1a;https://link.springer.com/book/10.1007/978-3-031-16174-2 本文是本书第四章的学习笔记。 感觉这一章写得不怎么样。以研究生组会讲异质图神经网络主题论文作为标准的话&#xff0c;倒是还行&#xff0c;介绍了HGNN的常见…

【面试宝典】吐血整理的100道Java多线程并发面试题

吐血整理的108道Java多线程&并发面试题前言1、Java中实现多线程有几种方法2、继承 Thread 类 流程3、实现 Runnable 接口4、ExecutorService、 Callable、 Future 有返回值线程5、基于线程池的方式6、4 种线程池7、如何停止一个正在运行的线程8、notify()和notifyAll()有什…

Clean-label Backdoor Attack against Deep Hashing based Retrieval论文笔记

论文名称Clean-label Backdoor Attack against Deep Hashing based Retrieval作者Kuofeng Gao &#xff08;Tsinghua University&#xff09;出版社arxiv 2021pdf在线pdf代码无 简介&#xff1a;本文提出了首个针对 hashing 模型的 clean-label backdoor attack。生成 targeted…