数据结构之单链表的详细实现(图解)

news2024/11/15 21:48:16

前言

本次博客讲结合图例讲解单向不带头非循环链表

此后会讲解一些题目

1单链表的实现

1.1什么是单链表

我们先看数组,即顺序表的是什么样的,再看链表

1.2单链表的特点

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

1. 单向、双向

2. 带头、不带头

3. 循环、非循环

我们今天可以讲解最复杂的情况单向不带头非循环链表

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

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

链表的本质就是牺牲空间节省时间,它的访问速度快

1.2链表的实现

头文件部分
#pragma once
#define _SECURE_NO_WARNINGS 
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int datatype;
struct slnode{
	datatype data;
	struct slnode* next;
};
void slinit(struct slnode**pphead);
void slprint(struct slnode* phead);
void slPushBack(struct slnode** pphead,datatype put);
void slPushfront(struct slnode** pphead, datatype put);
void slPopfront(struct slnode** pphead);
void slPopback(struct slnode** pphead);
struct slnode* slFind(struct slnode* phead,datatype n);
void slInsertfront(struct slnode**pphead, datatype find,datatype i);
void slErase(struct slnode** pphead, datatype find);

大家简略看看就好,后面还会再提到,主要关注链表的定义

接下来讲解如何实现该链表

 初始化链表 slinit()

由于 没有哨兵位指针,所以它的初始化只需要让头指针为空即可

void slnodeinit(struct slnode **pphead)

{

*pphead=NULL;

}
扩展空间  struct slnode* buyslnode(datatype put)

如果要尾插或者头插一个链表,我们必须要动态开辟空间,才能增添一个结点

思路

首先他它有开辟一个空间,就必须要有一个指针记住该空间的地址

所以让该函数返回一个指针,注意在开辟成功后,必须要

让开辟的空间的next成员指向空指针,不然它就是一个野指针

struct slnode* buyslnode(datatype put)
{
	struct slnode* newnode = (struct slnode*)malloc(sizeof(struct slnode));
	if (newnode == NULL)
	{
		return NULL;
	}
	newnode->data = put;//插入元素的大小
	newnode->next = NULL;//赋值w为空指针
	return newnode;
}
 头插void slPushfront(struct slnode** pphead, datatype put)

思路

如果一开始没有一个结点,也就是 phead此时为NULL

要让  *phead=buyslnode(put);

否则就是普通的头插,此时画图让大家理解头插

但此时 不管头结点是不是NULL指针都可以直接通过以下代码实现头插

void slPushfront(struct slnode** pphead, datatype put)
{
	struct slnode* newnode = buyslnode(put);
		newnode->next = *pphead;
		*pphead = newnode;
}
尾插void slPushBack(struct slnode** pphead,datatype put)

思路

仍然考虑phead为NULL的情况,此时只要让newnode=*pphead即可

但是它是需要找到尾巴也就是最后一个非0结点

怎么找,看图

void slPushBack(struct slnode** pphead,datatype put)
{
	struct slnode* newnode = buyslnode(put);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		struct slnode* cur = *pphead;
		while (cur->next)
		{
			cur = cur->next;
		}
		cur->next = newnode;
	}
}
打印 void slprint(struct slnode* phead)

其实只需要遍历一遍即可

直接看代码

但是要注意这里是cur!=NULL才算遍历完

void slprint(struct slnode* phead)
{
	struct slnode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

到这里应该要测试一遍了

看图

这里分别尾插1 2 3 4 5 最后头插一个 0 再把他们打印出来

尾删 void slPopback(struct slnode** pphead)

分析

首先要有结点才能删除,所以头结点可以通过assert判断

如果只有一个结点让头指针置空

如果有两个以上的结点那么就找到尾巴并且找到尾巴前一个结点让它置空,并删除尾结点

看图吧

看代码

void slPopback(struct slnode** pphead)
{
	    assert(*pphead);
		struct slnode* cur = *pphead;
		struct slnode* prev = *pphead;
		if (cur->next == NULL)
		{
			free(*pphead);
			pphead = NULL;
		}
		else
		{
			while (cur->next)
			{
				prev = cur;
				cur = cur->next;
			}
			free(cur);
			prev->next = NULL;
		}

}

头删 void slPopfront(struct slnode** pphead)

分析

首先还是要有结点可删才可以删,所以还是要assert

其次还是这样,如果只有一个结点那就直接free掉

如果有两个以上的结点,还要找到下一个结点

看图

这里头删只要一个变量就可

void slPopfront(struct slnode** pphead)
{
	 assert(*pphead);
	 if ((*pphead)->next == NULL)
	 {
		free(*pphead);
		*pphead = NULL;
	 }
	else
	{
		struct slnode*tem = (*pphead)->next;
		free(*pphead);
		*pphead = tem;
	}
}
 定位 struct slnode* slFind(struct slnode* phead, datatype n)

分析

遍历一遍 判断即可

struct slnode* slFind(struct slnode* phead, datatype n)
{
	    assert(phead);
		while (phead)
		{
			if (phead->data== n)
			{
				return phead;
			}
			else
				phead = phead->next;
		}
		printf("没有该数据\n");
		return NULL;
}
 在pos前插入 void slInsertfront(struct slnode**pphead, datatype find, datatype i)

分析

先找到该位置,如果没有找到位置,结束

找到该位置后

如果该位置是第一个位置就是头插

还得找到这个位置的前一个位置把他们相连

看图

void slInsertfront(struct slnode**pphead, datatype find, datatype i)
{
	struct slnode* pfind=slFind(*pphead, find);
	if (pfind == NULL)
		return;
	else
	{
		if (pfind == *pphead)
			slPushfront(pphead, i);
		else
		{
			struct slnode* cur = *pphead;
			while (cur->next != pfind)
			{
				cur = cur->next;
			}
			cur->next = buyslnode(i);
			cur->next->next = pfind;
		}
	}
}
删除该位置的数据 void slErase(struct slnode** pphead, datatype find)

分析

但凡要删除数据就必须要有数据,所以还是要assert一下头指针

如果是pos位置在头指针位置即为头删 如果是在最后一个结点即为尾删

如果在中间,就是必须要找到前一个和后一个结点

看图

看代码

void slErase(struct slnode** pphead, datatype find)
{
	struct slnode* pfind = slFind(*pphead, find);
	if (pfind == *pphead)
	{
		*pphead = pfind->next;
		free(pfind);
	}
	else if(pfind->next==NULL)
	{
		slPopback(pphead);
	}
	else
	{
		struct slnode* cur = *pphead;
		while (cur->next != pfind)
		{
			cur = cur->next;
		}
		cur->next = pfind->next;
		free(pfind);
	}
}

至此基本所有的功能都实现了

我们可以测试一下

看看首先 尾插 1 2 3 4 5然后头插一个0

此时链表为 0 1 2 3 4 5 

然后再头删 尾删 链表为  1 2 3 4

再在2的位置前插入10,删除4这个结点

最终结果为 1 2 10 3      ok对上了

总结

到这里单链表的实现就完成了,还是多练

祝大家开心

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

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

相关文章

Avalonia笔记2 -数据集合类控件

学习笔记&#xff1a; 1. DataGrid 笔记1中已经记录&#xff1b; 2. ItemsControl 属性&#xff1a; ItemsSource&#xff1a;数据源 ItemsControl.ItemTemplate&#xff1a;单项数据模板&#xff0c;内部使用<DataTemplate> 示例&#xff1a; <ItemsContr…

html页面使用@for(){},@if(){},利用jquery 获取当前class在列表中的下标

基于以前的项目进行修改优化&#xff0c;前端代码根据List元素在html里进行遍历显示 原先的代码&#xff1a; 其中&#xff0c;noticeGuide.Id是标识noticeGuide的唯一值&#xff0c;但是不是从0开始的【是数据库自增字段】 但是在页面初始化加载的时候&#xff0c;我们只想…

程序员35岁的职业困惑及应对之道

35岁,对许多程序员来说,是一个职业生涯的重要分水岭。在这个年龄,一些人开始感到迷茫和焦虑,担心自己的技能已经落后,难以跟上日新月异的技术变革。而另一些人则充满信心,认为多年来积累的丰富经验和扎实的技术功底,将助力他们在未来的职业道路上取得新的飞跃。 无疑,在AI、自…

Transformer的前世今生 day09(Transformer的框架概述)

前情提要 编码器-解码器结构 如果将一个模型分为两块&#xff1a;编码器和解码器那么编码器-解码器结构为&#xff1a;编码器负责处理输入&#xff0c;解码器负责生成输出流程&#xff1a;我们先将输入送入编码器层&#xff0c;得到一个中间状态state&#xff0c;并送入解码器…

11.Notepad++

文章目录 一、下载和安装设置练习 以前在记事本上写的代码看上去有点累&#xff0c;因为所有的单词看上去都是黑色的&#xff0c;并且当代码出现问题后&#xff0c;它提示第三行&#xff0c;我们还需要一行一行去数。这些问题都可以由一个高级记事本&#xff1a; Notepad 来解…

书生浦语大模型实战营第一课笔记

书生浦语大模型全链路开源体系 课程笔记大模型的发展趋势InternLM2的主要亮点模型到应用的典型流程全链路的开源工具 InternLM2技术报告笔记大型语言模型的发展InternEvoModel Structure训练数据 课程笔记 第一节课主要对大模型进行介绍&#xff0c;特别是书生浦语大模型的发展…

嵌入式学习46——硬件相关2串口通信

串口&#xff1a; 端口&#xff1a; COM 波特率&#xff1a; 9600 115200 &#xff08;bps&#xff09; 每秒传输的数据…

Qt开发(2)——在已有VS项目中配置Qt

在之前的Qt开发学习中&#xff0c;基本都是在Qt Creator中创建一个Qt项目&#xff0c;或者即便是在VS中也是直接新建一个Qt项目。但很少有记录如何在已有的C项目中添加Qt,这就好比我有个项目已经开发完了&#xff0c;现在又说加个Qt界面的功能。这篇文章就是记录如何在已有项目…

Unity3d C#转换微信小游戏 Dotween插件在苹果(IOS)设备中异常问题高性能模式修复

问题 使用minigame-unity-webgl-transform插件转换微信小游戏&#xff0c;功能在安卓和开发工具上都能正常&#xff0c;不过使用Dotween(版本DOTweenPro v1.0.244)插件实现的功能在苹果系统中却都不能正常对比如下&#xff1a; 云移动正常&#xff1a; 云移动IOS异常&#x…

远程监控电脑软件下载安装

员工随意下载和安装软件的行为&#xff0c;往往会给企业带来潜在的安全风险和管理难题。 为了防止员工随意下载软件&#xff0c;企业有必要实施远程监控&#xff0c;记录员工电脑上软件的下载和安装情况。 在此之前&#xff0c;先要明确远程监控的目标和意义。 通过监控员工电…

【搞不明白】redis和mysql、mybatisplus有啥关系

一个解决三天的bug&#xff0c;到现在也没有搞明白&#xff0c;如题&#xff0c;到底redis和mysql、mybatisplus能有啥关系。三个不相关的嘛 记录下问题&#xff1a; (框架用的Jeecgboot 3.6.0) – 学习使用 看下面的一段代码&#xff0c;是一个分页查询&#xff1a;注意引用的…

151 shell编程,正则表达式,在C语言中如何使用正则表达式

零&#xff0c;坑点记录&#xff1a;bash 和 dash 的区别&#xff0c;导致的坑点 查看当前用的shell 是啥&#xff0c;用的是/bin/bash hunandedehunandede-virtual-machine:~$ echo $SHELL /bin/bash 当shell 脚本运行的时候&#xff08;后面会学到方法&#xff0c;这里是最…

深入理解React的setState机制

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

【Python函数和类1/6】初始函数

目录 目标 导入 函数 内置函数 len()函数 type()函数 自定义函数 定义函数 函数定义规则 调用函数 小结 多次调用 函数的使用原则 总结 目标 从今天开始&#xff0c;我们将通过6篇博文&#xff0c;来一起学习一下函数的相关知识。今天&#xff0c;我们主要从函数概…

Matlab|计及电池储能寿命损耗的微电网经济调度

目录 1 主要内容 储能寿命模型 负荷需求响应 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序参考文献《考虑寿命损耗的微网电池储能容量优化配置》模型&#xff0c;以购售电成本、燃料成本和储能寿命损耗成本三者之和为目标函数&#xff0c;创新考虑储能寿命损耗约…

一键换脸的facefusion

FaceFusion 一个开源换脸软件&#xff0c;提供UI界面&#xff0c;启动后可直接在浏览器上面上传图片进行换脸操作。 电脑环境win10&#xff0c;软件pycharm&#xff0c;需要提前安装好python环境&#xff0c;推荐使用Anaconda3。关注文章下方公共号发送 “ 软件安装包 ”可以获…

【Java跳槽面试必备】2024年最新八股文

Java基础面试题 Java的特点 Java 与 C 的区别 JDK/JRE/JVM三者的关系 Java程序是编译执行还是解释执行&#xff1f; 面向对象和面向过程的区别&#xff1f; 面向对象有哪些特性&#xff1f; 数组到底是不是对象&#xff1f; Java的基本数据类型有哪些&#xff1f; 为什么不能用…

C. Grouping Increases

Here 解题思路 两个序列&#xff0c;保持顺序对于代价的产生进行考虑当添入一个大于当前序列最后值的数&#xff0c;代价加1&#xff0c;但下次判断标准变大当添入一个小于当前序列最后值的数&#xff0c;代价不增&#xff0c;但下次判断标准变小考虑形象化描述将两个序列看作…

Qt 文件操作

文件概述 文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库&#xff0c;提供了跨平台的文件操作能力。Qt 提供了很多关于文件的类&#xff0c;通过这些类能够对文件系统进行操作&#xff0c;如文件读写、文件信息获取、文件复制或重命名等。 输入输出设备类 在 Qt …

iOS_convert point or rect 坐标和布局转换+判断

文章目录 1. 坐标转换2. 布局转换3. 包含、相交 如&#xff1a;有3个色块 let view1 UIView(frame: CGRect(x: 100.0, y: 100.0, width: 300.0, height: 300.0)) view1.backgroundColor UIColor.cyan self.view.addSubview(view1)let view2 UIView(frame: CGRect(x: 50.0, …