数据结构之单链表及其实现!

news2024/10/1 12:15:35

目录

​编辑

1.  顺序表的问题及思考

2.链表的概念结构和分类

2.1 概念及结构

2.2 分类

3. 单链表的实现

3.1 新节点的创建

3.2 打印单链表

3.3 头插

3.4 头删

3.5 尾插

3.6 尾删

3.7 查找元素X

3.8 在pos位置修改

3.9 在任意位置之前插入

3.10 在任意位置删除

3.11 单链表的销毁

4. 完整代码

5. 完结散花


                                            悟已往之不谏,知来者犹可追  

创作不易,宝子们!如果这篇文章对你们有帮助的话,别忘了给个免费的赞哟

1.  顺序表的问题及思考

问题:

1. 中间/头部的插入删除,时间复杂度为O(N)

2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。

3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到 200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

思考:如何解决以上问题呢?下面给出了链表的结构来看看。

2.链表的概念结构和分类

2.1 概念及结构

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

2.2 分类

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

1. 单项或者双向

 2. 带头或者不带头

 3. 循环或者非循环

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

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

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

下面我就来实现一下无头单向非循环链表~

3. 单链表的实现

这里我就具体实现一下接口~(SList.h)

#define _CRT_SECURE_NO_WARNINGS
#pragma once//避免头文件被多次引用
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int SListDataType;//便于改变数据类型

//定义一个结构体类型的节点
typedef struct SListNode
{
	SListDataType data;
	struct SListNode* next;//存储下一个节点的地址
}SListNode;

//1. 新节点的创建
SListNode* SListCreatNode(SListDataType x);

//2. 打印单链表
void PrintSList(SListNode* phead);

//3. 头插
void SListPushFront(SListNode** phead, SListDataType x);

//4. 头删
void  SListPopFront(SListNode** phead);

//5. 尾差
void SListPushBack(SListNode** phead, SListDataType x);

//6. 尾删
void  SListPopBack(SListNode** phead);

//7. 查找元素X
SListNode* SListFind(SListNode* phead, SListDataType x);

//8. 在pos位置修改
void SListModify(SListNode* pos, SListDataType x);

//9. 在任意位置之前插入
void SListInsert(SListNode** phead, SListNode* pos, SListDataType x);

//10. 在任意位置删除
void SListErase(SListNode** phead, SListNode* pos);

//11. 销毁单链表
void SListDestroy(SListNode** phead);

3.1 新节点的创建

(1)代码实现

//1. 新节点的创建
SListNode*  SListCreatNode(SListDataType x)
{
	SListNode* NewNode= (SListNode*)malloc(sizeof(SListNode));//开辟空间
	if (NewNode == NULL)//判断空间是否开辟成功
	{
		perror("malloc fail");
		return NULL;
	}
	NewNode->data = x;//赋值
	NewNode->next = NULL;//置空
	return NewNode;
}

3.2 打印单链表

(1)代码实现

//2. 打印单链表
void PrintSList(SListNode* phead)
{
	if (phead == NULL)
	{
		printf("NULL");//如果链表没有元素就打印NULL
		return;
	}
	SListNode* cur = phead;
	//循环单链表打印
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

(2) 复杂度分析

  • 时间复杂度:因为需要遍历整个链表,显然时间复杂度为O(N)。
  • 空间复杂度:打印链表不需要格外的空间,所以空间复杂度为O(1).

3.3 头插

(1)代码实现

//3. 头插
void SListPushFront(SListNode** phead, SListDataType x)
{
	assert(phead);
	SListNode* newnode =SListCreatNode(x);//创建一个新节点
	newnode->next =*phead;
	*phead = newnode;
}

(2) 复杂度分析

  • 时间复杂度:不需要额外时间的消耗,时间复杂度为O(1)。
  • 空间复杂度:固定创造一个节点,空间复杂度为O(1)。

3.4 头删

(1)代码实现

//4. 头删
void  SListPopFront(SListNode** phead)
{
	assert(phead);
	assert(*phead);//如果没有数据就不用头删,并报错
	SListNode* cur = (*phead)->next;
	free(*phead);
	*phead = cur;
}

(2) 复杂度分析

  • 时间复杂度:不需要额外时间的消耗,时间复杂度为O(1)。
  • 空间复杂度:不需要格外的空间消耗,空间复杂度为O(1)。

3.5 尾插

(1)代码实现

//5. 尾插
void SListPushBack(SListNode** phead, SListDataType x)
{
	assert(phead);
	if (*phead == NULL)
	{
		*phead = SListCreatNode(x);//创建新节点并插入
	}
	else
	{
		SListNode* tail = *phead;
		while (tail->next != NULL)//找到尾节点
		{
			tail = tail->next;
		}
		tail->next = SListCreatNode(x);//创建新节点并插入
	}
}

(2) 复杂度分析

  • 时间复杂度:需要遍历整个链表,时间复杂度为O(N)。
  • 空间复杂度:固定创造一个节点,空间复杂度为O(1)。

3.6 尾删

(1)代码实现

//6. 尾删
void  SListPopBack(SListNode** phead)
{
	assert(phead);
	assert(*phead);//链表为空就不进行尾删
	SListNode* tail = *phead;
	if (tail->next == NULL)//如果链表就只有一个元素就进行头删
	{
		SListPopFront(phead);
	}
	else
	{
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

(2) 复杂度分析

  • 时间复杂度:需要遍历整个链表,时间复杂度为O(N)。
  • 空间复杂度:不需要格外的空间消耗,空间复杂度为O(1)。

3.7 查找元素X

(1)代码实现

//7. 查找元素X
SListNode* SListFind(SListNode* phead, SListDataType x)
{
	assert(phead);
	while (phead->next!=NULL)//注意最后一个节点是没有查找的
	{
		if (phead->data == x)
			return phead;
			phead = phead->next;
	}
	if (phead->data == x)
		return phead;//最后一个节点没有查找
	else
	return NULL;//没找到
}

(2) 时间复杂度

  • 时间复杂度:可能需要遍历整个链表,时间复杂度为O(N)。
  • 空间复杂度:不需要格外的空间消耗,空间复杂度为O(1)。

3.8 在pos位置修改

(1)代码实现


//8. 在pos位置修改
void SListModify( SListNode* pos, SListDataType x)
{
	assert(pos);
	pos->data = x;
}

(2) 时间复杂度

  • 时间复杂度:可能需要遍历整个链表,时间复杂度为O(N)。
  • 空间复杂度:不需要格外的空间消耗,空间复杂度为O(1)。

3.9 在任意位置之前插入

(1)代码实现

//9. 在任意位置之前插入
void SListInsert(SListNode** phead, SListNode* pos, SListDataType x)
{
	assert(phead);
	assert(*phead);
	if (pos==*phead)//如果pos位置刚好是第一个节点就进行头插
	{
		SListPushFront( phead,x);
	}
	else
	{
		SListNode* newnode = SListCreatNode(x);
		SListNode* cur = *phead;
		while (cur->next != pos)//找到pos前一个节点
		{
			cur = cur->next;
		}
		cur->next = newnode;
		newnode->next  = pos;
	}
}

(2) 复杂度分析

  • 时间复杂度:可能需要遍历整个链表,时间复杂度为O(N)。
  • 空间复杂度:不需要格外的空间消耗,空间复杂度为O(1)。

3.10 在任意位置删除

(1)代码实现

//10. 在任意位置删除
void SListErase(SListNode** phead, SListNode* pos)
{
	assert(phead && *phead && pos);
	if (pos==*phead)//如果pos位置就是第一个节点就进行头删
	{
		SListPopFront(phead);
	}
	else
	{
		SListNode* cur = *phead;
		while (cur->next != pos)//找到pos前一个节点
		{
			cur = cur->next;
		}
		cur->next = pos->next;
		free(pos);
	}
}

(2) 复杂度分析

  • 时间复杂度:可能需要遍历整个链表,时间复杂度为O(N)。
  • 空间复杂度:不需要格外的空间消耗,空间复杂度为O(1)。

3.11 单链表的销毁

(1)代码实现

//11. 销毁单链表
void SListDestroy(SListNode** phead)
{
	assert(*phead && phead);
	SListNode* cur = *phead;
	while (cur!= NULL)
	{
		SListNode* tmp = cur->next;
		free(cur);
		cur = tmp;
	}
	*phead = NULL;
}

(2) 复杂度分析

  • 时间复杂度:可能需要遍历整个链表,时间复杂度为O(N)。
  • 空间复杂度:不需要格外的空间消耗,空间复杂度为O(1)。

4. 完整代码

SList.c

#define _CRT_SECURE_NO_WARNINGS

#include"SList.h"

//1. 新节点的创建
SListNode*  SListCreatNode(SListDataType x)
{
	SListNode* NewNode= (SListNode*)malloc(sizeof(SListNode));//开辟空间
	if (NewNode == NULL)//判断空间是否开辟成功
	{
		perror("malloc fail");
		return NULL;
	}
	NewNode->data = x;//赋值
	NewNode->next = NULL;//置空
	return NewNode;
}

//2. 打印单链表
void PrintSList(SListNode* phead)
{
	if (phead == NULL)
	{
		printf("NULL");//如果链表没有元素就打印NULL
		return;
	}
	SListNode* cur = phead;
	//循环单链表打印
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

//3. 头插
void SListPushFront(SListNode** phead, SListDataType x)
{
	assert(phead);
	SListNode* newnode =SListCreatNode(x);//创建一个新节点
	newnode->next =*phead;
	*phead = newnode;
}

//4. 头删
void  SListPopFront(SListNode** phead)
{
	assert(phead);
	assert(*phead);//如果没有数据就不用头删,并报错
	SListNode* cur = (*phead)->next;
	free(*phead);
	*phead = cur;
}

//5. 尾插
void SListPushBack(SListNode** phead, SListDataType x)
{
	assert(phead);
	if (*phead == NULL)
	{
		*phead = SListCreatNode(x);//创建新节点并插入
	}
	else
	{
		SListNode* tail = *phead;
		while (tail->next != NULL)//找到尾节点
		{
			tail = tail->next;
		}
		tail->next = SListCreatNode(x);//创建新节点并插入
	}
}

//6. 尾删
void  SListPopBack(SListNode** phead)
{
	assert(phead);
	assert(*phead);//链表为空就不进行尾删
	SListNode* tail = *phead;
	if (tail->next == NULL)//如果链表就只有一个元素就进行头删
	{
		SListPopFront(phead);
	}
	else
	{
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

//7. 查找元素X
SListNode* SListFind(SListNode* phead, SListDataType x)
{
	assert(phead);
	while (phead->next!=NULL)//注意最后一个节点是没有查找的
	{
		if (phead->data == x)
			return phead;
			phead = phead->next;
	}
	if (phead->data == x)
		return phead;//最后一个节点没有查找
	else
	return NULL;//没找到
}

//8. 在pos位置修改
void SListModify( SListNode* pos, SListDataType x)
{
	assert(pos);
	pos->data = x;
}

//9. 在任意位置之前插入
void SListInsert(SListNode** phead, SListNode* pos, SListDataType x)
{
	assert(phead);
	assert(*phead);
	if (pos==*phead)//如果pos位置刚好是第一个节点就进行头插
	{
		SListPushFront( phead,x);
	}
	else
	{
		SListNode* newnode = SListCreatNode(x);
		SListNode* cur = *phead;
		while (cur->next != pos)//找到pos前一个节点
		{
			cur = cur->next;
		}
		cur->next = newnode;
		newnode->next  = pos;
	}
}

//10. 在任意位置删除
void SListErase(SListNode** phead, SListNode* pos)
{
	assert(phead && *phead && pos);
	if (pos==*phead)//如果pos位置就是第一个节点就进行头删
	{
		SListPopFront(phead);
	}
	else
	{
		SListNode* cur = *phead;
		while (cur->next != pos)//找到pos前一个节点
		{
			cur = cur->next;
		}
		cur->next = pos->next;
		free(pos);
	}
}

//11. 销毁单链表
void SListDestroy(SListNode** phead)
{
	assert(*phead && phead);
	SListNode* cur = *phead;
	while (cur!= NULL)
	{
		SListNode* tmp = cur->next;
		free(cur);
		cur = tmp;
	}
	*phead = NULL;
}

5. 完结散花

好了,这期的分享到这里就结束了~

如果这篇博客对你有帮助的话,可以用你们的小手指点一个免费的赞并收藏起来哟~

如果期待博主下期内容的话,可以点点关注,避免找不到我了呢~

我们下期不见不散~~

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

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

相关文章

【Primsjs】vue+代码高亮

效果 括号变色括号鼠标移入高亮效果 代码效果 目录树 在这里插入图片描述 安装 cnpm i primsjs简介 文档&#xff08;点我进去&#xff09; 备用地址-https://prismjs.com/docs/index.html Primsjs使用 导入配置插件 注意&#xff1a;需要什么插件就导入什么插件 码 &l…

Linux centos6安装rz、sz命令

centos6传文件提示command not found # yum install lrzsz 提示错误 wget http://www.ohse.de/uwe/releases/lrzsz-0.12.20.tar.gz 下载离线包 https://www.ohse.de/uwe/software/lrzsz.html 下载最新版本 [rootnode1 ~]# tar -zxvf lrzsz-0.12.20.tar.gz …

PV与PVC知多少?解锁CKA认证考点攻略!

往期精彩文章 : 提升CKA考试胜算&#xff1a;一文带你全面了解RBAC权限控制&#xff01;揭秘高效运维&#xff1a;如何用kubectl top命令实时监控K8s资源使用情况&#xff1f;CKA认证必备&#xff1a;掌握k8s网络策略的关键要点提高CKA认证成功率&#xff0c;CKA真题中的节点维…

蓝桥省赛倒计时 35 天-双指针

双指针介绍 双指针算法是一种常用的算法技巧&#xff0c;它通常用于在数组或字符串中进行快速查找、匹配、排序或移动操作。 pointer 双指针并非真的用指针实现&#xff0c;一般用两个变量来表示下标&#xff08;在后面都用指针来表示&#xff09;。 双指针算法使用两个指针在数…

【Java JVM】Class 文件

Java 的口号 “一次编写, 到处运行 (Write Once, Run Anywhere)” 的基础: JVM 和 所有平台都统一支持的程序存储格式 – 字节码 (Byte Code)。 只要在对应的平台安装对应的 JVM, 将我们编写的源码编译为 Class 文件, 就能达到了一次编写, 导出运行的目标, 中间的所有细节由不同…

0103n阶行列式-行列式-线性代数

文章目录 一 n阶行列式二 三阶行列式三 特殊行列式结语 一 n阶行列式 ∣ a 11 a 12 ⋯ a 1 n a 21 a 22 ⋯ a 2 n ⋯ ⋯ ⋯ ⋯ a n 1 a n 2 ⋯ a n n ∣ \begin{vmatrix}a_{11}&a_{12}&\cdots&a_{1n}\\a_{21}&a_{22}&\cdots&a_{2n}\\\cdots&\cdots…

大型房企知识竞赛活动方案

&#xff08;一&#xff09; 线上挑战赛 1、加入团队 个人可以依据自身情况选择加入初始团队&#xff0c;也可创建团队。 2、题库来源 参考权威题库&#xff0c;适当加入公司帮扶贫困县的相关历史数据题目 3、小程序活动专题页 模块包括&#xff1a;党史知识线上挑战赛、活动宣…

Unity 采用自定义通道ShaderGraph实现FullScreen的窗户雨滴效果

效果如下 ShaderGraph实现 N21 随机化 DragLayer分层 将DragLayer分成四层&#xff0c;分别调整每层的缩放和大小 Shader实现的链接&#xff08;Unity 雨水滴到屏幕效果&#xff09; 我也是参考这个实现Shader Graph

Android7.1 ANR error 弹窗处理

Android7.1 ANR error 弹窗处理 问题描述解决方法 郑重声明:本人原创博文&#xff0c;都是实战&#xff0c;均经过实际项目验证出货的 转载请标明出处:攻城狮2015 Platform: Rockchip OS:Android 7.1.2 Kernel: 3.10 问题描述 有时会用到第三方apk&#xff0c;内置到系统中&…

通信-CAN-00 标准概述

总结了下CAN的基本知识&#xff0c;实际CAN的标准&#xff0c;内容&#xff0c;工具使用&#xff0c;上位机开发&#xff0c;下位机开发等&#xff0c;后续会找时间慢慢更新。本文主要介绍CAN标准&#xff0c;并对11898进行了进一步的介绍。 1 CAN概念 CAN-Controller Area N…

C++ 多状态dp

目录 按摩师 打家劫舍 打家劫舍2 删除并获得点数 粉刷房子 按摩师 面试题 17.16. 按摩师 最大值问题 f : 预约此次的最长时间 g &#xff1a;不预约此次的最长时间 出现的错误&#xff1a;return max(f[n - 1]), g[n - 1]); 注意&#xff1a;①题目没给nums的范围&…

软件杯 图像识别-人脸识别与疲劳检测 - python opencv

文章目录 0 前言1 课题背景2 Dlib人脸识别2.1 简介2.2 Dlib优点2.3 相关代码2.4 人脸数据库2.5 人脸录入加识别效果 3 疲劳检测算法3.1 眼睛检测算法3.3 点头检测算法 4 PyQt54.1 简介4.2相关界面代码 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是…

【C++】深度解剖多态

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;了解什么是多态&#xff0c;熟练掌握多态的定义&a…

预约自习室

预约自习室 1、技术介绍 自习室预约系统的后端开发语言采用Node&#xff0c;后端开发框架采用Express&#xff0c;数据库采用的Node的最佳搭档MySQL。采用Vue作为前端开发框架&#xff0c;Element-UI作为开发的组件库&#xff0c;微信小程序。期间采用axios实现网页数据获取&a…

[java入门到精通] 19 网络编程,设计模式

今日目标 网络编程 TCP通信 Junit单元测试 单例设计模式 多例设计模式 工厂设计模式 1 网络编程 1.1 软件架构 C/S结构 &#xff1a;全称为Client/Server结构&#xff0c;是指客户端和服务器结构。常见程序有&#xff31;&#xff31;、迅雷等软件B/S结构 &#xff1a…

Biomedical knowledge graph-enhanced prompt generation for large language models

1. 生物医学知识图谱增强大语言模型提示生成 论文地址&#xff1a;[2311.17330] Biomedical knowledge graph-enhanced prompt generation for large language models (arxiv.org) 源码地址&#xff1a;https://github.com/BaranziniLab/KG_RAG 2. 摘要 大语言模型&#xff0…

MySQL--索引优化实战篇(1)

前言&#xff1a; 我们常说的SQL优化&#xff0c;简单来说就是索引优化&#xff0c;通过合理创建索引&#xff0c;调整SQL语法等&#xff0c;来提升查询效率&#xff0c;想要进行SQL优化&#xff0c;就必须知道索引的原理&#xff0c;而且能够看懂SQL的执行计划。 MySQL–索引…

基于springboot的校园二手交易平台(程序+数据库+文档)

** &#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;** 一、研究背景…

STM32OLED调试工具

OLED介绍 4个引脚的oled &#xff1a; GND引脚接地&#xff0c;VCC接3.3v的电源 SCL与SDA是I2C通信的引脚 使用OLED显示屏驱动函数模块 接线图 将oled函数调试的代码引入到工程项目中 oled工程代码 OLED.C文件代码 #include "stm32f10x.h" #include "OLED_Font…

论文笔记 Where Would I Go Next? Large Language Models as Human Mobility Predictor

arxiv 2023 08的论文 1 intro 1.1 人类流动性的独特性 人类流动性的独特特性在于其固有的规律性、随机性以及复杂的时空依赖性 ——>准确预测人们的行踪变得困难近期的研究利用深度学习模型的时空建模能力实现了更好的预测性能 但准确性仍然不足&#xff0c;且产生的结果…