链表(数据结构)

news2024/11/12 13:49:41

目录

链表

链表的分类

1、单向或者双向

2、带头或者不带头

3、循环或者非循环

总结:

单链表

创建链式结构

创建新节点

尾插

尾删

头插

头删

查找节点

在pos位置后插入

删除pos位置后的节点

销毁


链表

概念:

链表是一种物理结构上非连续的、非顺序的存储结构,数据元素的逻辑顺序是通过链表结构中的指针链接起来的!前一个元素中存放着后一个元素的地址!

结构:

结构中存放着链表下一个元素的地址!用一个指针来存储!

一般情况下一个链表结构中存放的是元素的值以及下一个位置的指针!或者多个链接元素的指针!

链表的分类

1、单向或者双向

单向链表:

链表结构中只有一个指针,该指针是存放的是下一个元素的地址的指针变量!


看图理解:


双向链表:

结构中有两个指针,分别记录它的前一个元素和后一个元素的地址!


看图理解:


2、带头或者不带头

1、带哨兵位的头节点的单向链表:


2、带哨兵位头节点的双向链表:


3、循环或者非循环

1、单向循环链表


2、双向循环链表 


3、单向带头循环链表 


4、带头双向循环链表 


总结:

1、图形

以上图都是链表的逻辑结构,物理上也就是底层并非是这样指向的,而是每个指针里面都存储它所对应节点的地址!


2、种类

链表类型总共有八种,分别是按照以上的三大类来组合:

第一类:单向或双向

第二类:带头或不带头

第三类:循环或不循环


八种链表:

1、单向链表

2、双向链表

3、单向带头链表

4、双向带头链表

5、单向循环链表

6、双向循环链表

7、单向带头循环链表

8、双向带头循环链表


本篇博主只带大家写一种链表:单链表,因为只要会了一种来链表,其它链表都是大同小异的!


单链表

概念:

单链表中的节点,后一个节点中存的是前一个节点的地址,最后一个节点的中的地址为空!


逻辑结构:


物理结构:

 

 


总结:

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

2、现实中的节点一般是从堆上申请出来的

3、从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续!


下面我们用代码实现单链表!


创建链式结构

思路:

我们用一个结构体来封装链表节点的值以及该类型结构的地址,值是链表节点中要保存的值,结构体的的地址是用来存储前一个结构节点的地址,若该节点是最后一个则地址里面的值是NULL


代码:

//用typedef重命名,后续可以继续使用
typedef int SLDateType;

//创建链表结构
typedef struct SListNode
{
	SLDateType val;//节点值
	struct SListNode* next;//下一个节点的位置

}SLNode;

创建新节点

思路:

创建新节点,要将链表链起来,我们就得要创造新的节点进行链接,才能形成链接功能。该功能可供我们用于头插,尾插,等功能中!


代码:

//创建新节点
SLNode* CreateNode(SLDateType x)
{
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	//判断是否开辟成功
	if (newnode == NULL)
	{
		perror("malloc fali:");
		return;
	}
	//开始赋值
	newnode->val = x;
	newnode->next = NULL;

	//返回
	return newnode;
}

尾插

思路:

单链表进行尾插,先要找到单链表的尾节点,随后在创建一个要插入的节点,让找到的尾节点的指针指向要插入的新节点,也就是尾节点中的结构体指针变量中保存要插入的新节点的地址!


代码:

//尾插
void SListPushBank(SLNode** pphead, SLDateType x)
{
	//创建新节点
	SLNode* newnode = CreateNode(x);

	//判断是否是空链表,空链表直接进行尾插
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾
		SLNode* cur = *pphead;
		while (cur->next != NULL)
		{
			cur = cur->next;
		}
		
		//进行链接
		cur->next = newnode;
	}
}

尾删

思路:

单链表进行尾删,先进行循环找尾节点,同时用一个指针来记录尾节点的前一个节点的地址。找到尾节点之后,将尾节点的前一个节点中的指针置为NULL。再将尾节点的空间进行free释放!最后将尾节点结构中的指针置为NULL


代码:

//尾删
void SListPopBank(SLNode** pphead)
{
	//如果是NULL链表就不能再删
	assert(*pphead);

	//判断一个的情况
	if ((*pphead)->next == NULL)
	{
		free((*pphead)->next);
		*pphead= NULL;
	}
	else
	{
		//找尾
		SLNode* cur = *pphead;
		SLNode* tail = NULL;
		while (cur->next != NULL)
		{
			tail = cur;
			cur = cur->next;
		}

		//释放节点
		free(cur);
		cur = NULL;

		//将前一个节点置为NULL
		tail->next = NULL;
	}

}

头插

思路:

单链表头插,就是创建一个新节点,让这个新节点结构中的指针指向头节点,再让新节点成为头节点即可!


代码:

//头插
void SListPushFront(SLNode** pphead, SLDateType x)
{
	SLNode* newnode = CreateNode(x);

	SLNode* tail = *pphead;
	*pphead = newnode;
	newnode->next = tail;

}

头删

思路:

单链表头删,用一个指针指向头后面的节点,随后将头节点空间释放,再让后面的节点成为头即可!


代码:

//头删
void SListPopFront(SLNode** pphead)
{
	//如果是空链表就不进行删除
	assert(*pphead);

	SLNode* cur = (*pphead)->next;
	SLNode* tail = *pphead;
	free(tail);
	tail = NULL;
	*pphead = cur;
}

查找节点

思路:

循环遍历链表,找到我们要查找的节点,返回该节点的地址即可!若找不到返回NULL


代码:

//节点查找
SLNode* SListFind(SLNode* phead, SLDateType x)
{
	SLNode* cur = phead;
	while (cur)
	{
		if (cur->val == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

在pos位置后插入

思路:

用一个指针记录pos位置的下一个节点,随后让pos节点结构体的指针指向新插入的节点。再让新插入节点结构的指针指向pos位置的下一个节点完成链接!


代码:
 

// 在pos位置之后插入x
void SListInsertAfter(SLNode* pos, SLDateType x)
{
	//后面插入pos为NULL 不行
	assert(pos);

	SLNode* newnode = CreateNode(x);
	SLNode* cur = pos->next;
	pos->next = newnode;
	newnode->next = cur;
}

删除pos位置后的节点

思路:

用一个指针记录pos位置的下一个节点。再用一个指针记录pos位置的下下个节点,也就是pos位置的下一个节点的下一个节点,随后将pos位置的下一个节点空间释放。让pos位置节点结构中的指针指向pos位置的下下个节点即可!


代码:

// 删除pos位置之后的值
void SListEraseAfter(SLNode* pos)
{
	//pos为NULL 不可删后面的
	assert(pos);
	SLNode* cur = pos->next;

	if (cur == NULL)
	{
		free(cur);
		cur = NULL;
		pos->next = NULL;
	}
	else
	{
		SLNode* tail = cur->next;
		free(cur);
		pos->next = tail;
	}
}

销毁

思路:

循环遍历链表,用一个指针记录销毁节点的后一个节点,一个指针记录销毁节点,让两者都往后走,依次循环进行销毁(释放节点空间)!


代码:

// 销毁
void SListDestroy(SLNode* phead)
{
	SLNode* cur = phead;
	SLNode* tail = cur;
	while (cur)
	{
		tail = cur;
		cur = cur->next;
		free(tail);
	}
	tail = NULL;
	phead = NULL;
}

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

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

相关文章

c# 数据保存为PDF(二) (Aspose pdf篇)

文章目录 前言关于Aspose PDF使用Aspose.Pdf常用的命名空间和类库1 创建简单的PDF文档2 美化PDF样式2.1 创建测试数据2.2 项目头部样式2.3 全部代码 小结附录参考 前言 项目中需要将数据导出存为PDF格式,试了一下Aspose组件,仅以此记录一下使用感受。 …

设计模式——原型模式(浅拷贝和深拷贝)

是什么? 用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象; 结构 抽象原型类:规定了具体原型对象必须实现的Clone()方法; 具体原型类:实现抽象…

Elasticsearch:NLP 和 Elastic:入门

自然语言处理 (Natural Language Processing - NLP) 是人工智能 (AI) 的一个分支,专注于尽可能接近人类解释的理解人类语言,将计算语言学与统计、机器学习和深度学习模型相结合。 AI - Artificial Inteligence 人工智能ML - Machine Learning 机器学习DL…

集线器、网桥、交换机

一.集线器 集线器(HUB),它是工作在物理层的设备, 由于它只是工作在物理层的设备,所以它并不关心也不可能关心OSI上面几层所涉及的,它的工作机制流程是:从一个端口接收到数据包时,会在…

STL基础

目录 一、STL的诞生 二、STL基本概念 三、STL六大组件 大体分为六大组件:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器 四、容器、算法与迭代器的认识 容器container:存放数据地方 算法algorithm:解…

中级软件设计师备考---软件工程2

目录 软件测试分类和要求测试用例设计测试阶段McCabe复杂度软件维护软件过程改进---CMMICMM英文版CMM中文版CMMI 软件测试分类和要求 分类: 灰盒测试:多用于集成测试阶段,不仅关注输出、输入的正确性,同时也关注程序内部的情况。…

MySQL:插入,更新与删除、索引

一、学习目标 掌握如何向表中插入数据掌握更新数据的方法熟悉如何删除数据掌握对数据表基本操作的方法和技巧了解什么是索引掌握创建索引的方法和技巧熟悉如何删除索引熟悉掌握索引的常见问题 二、实验内容 创建表books,对数据表进行插入、更新和删除操作&#x…

SkyWalking集成Logback的使用

SkyWalking集成Logback的使用 将微服务的日志框架去集成SkyWalking,我们希望在我们微服务中日志中,能够记录当前调用链路的id,然后我们再根据这个id去SkyWalking的前端界面中进行搜索找到对应的调用链路记录。 因为springboot默认实现的日志…

Ansible 的脚本 之playbook 剧本

目录 第一章.playbooks的组成 1.1playbooks的组成部分 1.2运行playbook 1.3定义、引用变量 1.4.指定远程主机sudo切换用户 1.6.when条件判断 1.7.迭代 第二章.Templates模块 2.1.准备模板 2.2.修改主机清单文件 2.3.编写 playbook 第三章.tags 模块 3.1.yaml文件编…

操作系统原理 —— 线程的概念、实现方式、多线程模型(十)

什么是线程,为什么要引入线程? 有的进程可能需要 “同时” 做很多事情,而传统的进程只能串行的执行一系列的程序,为此,引入了 “线程” ,来增加并发度。 可以把线程理解为 轻量级进程,线程是可…

剧本杀闯关小程序app软件

剧本杀闯关小程序软件是一种结合了角色扮演和解谜游戏元素的互动娱乐产品,目前在市场上越来越受欢迎。以下是剧本杀闯关小程序软件市场行业情况的一些特点: 市场需求增长:随着人们对于线上互动娱乐的需求增加,剧本杀闯关小程序…

maven install的时候报Unable to find main class

目录 问题描述解决办法解决方案一:添加一个主函数解决方案二:将不是web工程的设置跳过解决方案三:打包插件的作用本质上就是将当前项目所依赖的jar打包到一块,这样jar包就可以运行了,我们完全可以把parent的pom.xml的b…

智慧农业物联网平台建设方案

本资料来源公开网络,仅供个人学习,请勿商用,如有侵权请联系删除。 智慧农业物联网系统组网图 2.2.1 智能温室组网说明 该组网图演示的为小面积示范区,每个连栋温室为 1个灌溉区域,1个子系统,该子系统完成…

学习Typescript(第一弹)

Typescript的基础类型 Boolean类型 let bool: boolean true;Number类型 let num: number 222;String类型 let str: string 码上coding;数组Array类型 对数组类型的定义有两种方式: let arr1: number[] [1,2,3]; let arr2: Array<number | string> [1,2,码上co…

Android APK 反编译后重新打包并签名

APKTool&#xff1a; Apktool 是一个逆向android非常有用的工具&#xff0c;可以用来反编译apk文件&#xff0c;并且能在修改部分资源文件后&#xff0c;重新打包成一个新的apk。 下载连接&#xff1a;http://ibotpeaches.github.io/Apktool/install/ 下载之后文件夹非常清爽&…

电力节能设备远程监控系统解决方案

电力节能设备远程监控系统解决方案 一、项目背景 随着城市化进程的发展&#xff0c;对电力设备安全、可靠、经济运行的要求越来越高&#xff0c;由于没有统一专业的用电现代化管理规划&#xff0c;电力设备管理混乱、数据采集不方便、运行智能化程度低&#xff0c;需要实时掌…

复合查询--- MySQL总结(三)

复合查询 文章目录 复合查询多表查询自连接子查询单行查询多行查询多列子查询合并查询 多表查询 前面讲述的关于进行一个表的简单查询和关于时间函数的相关问题&#xff0c;下面要进行复合查询的相关内容。 这里要使用卡笛尔集的概率让两个表融合成为一个表。 需要使用相应的字…

vue项目为例解决element ui 时间选择器 picker使用样式穿透不起作用问题

今天在开发中 需要修改时间选择器弹出的这个组件的样式 但这个东西比较坑爹 首先 不能影响其他组件 就是其他组件用了时间选择器 不能受到我们写的样式的影响 那么 就只好穿透了 但你会发现 这东西是作用与body下的 就很坑 穿透我试了挺久的 不起作用 但官方文档有提供给我们一…

centos7 配置 sftp 服务器并通过 java 上传文件

虚拟机配置 1、创建sftp用户组 groupadd sftp2、添加用户 useradd -g sftp mysftp3、设置用户密码 passwd mysftp4、创建文件根目录 mkdir /app/data5、将该目录设置到用户上 usermod -d /app/data mysftp6、修改配置文件 vim /etc/ssh/sshd_config7、找到 Subsystem &am…

算法记录 | Day50 动态规划

123.买卖股票的最佳时机III 思路&#xff1a; 1.确定dp数组以及下标的含义 最多可完成两笔交易意味着总共有三种情况&#xff1a;买卖一次&#xff0c;买卖两次&#xff0c;不买卖。 具体到每一天结束总共有 5 种状态&#xff1a; 未进行买卖状态&#xff1b;第一次买入状…