线性表之链表详解

在这里插入图片描述

欢迎来到我的:世界

希望作者的文章对你有所帮助,有不足的地方还请指正,大家一起学习交流 !


目录

  • 前言
    • 线性表的概述
    • 链表的概述
  • 内容
    • 链表的结构
      • 链表节点的定义
    • 链表的基本功能
      • 单向链表的初始化
      • 链表的插入操作
        • 头插操作
        • 尾插操作
      • 链表的删除操作
        • 头删操作
        • 尾删操作
      • 链表的查找操作
      • 链表的在指定位置的插入
        • 在指定位置之前插入
        • 在指定位置之后插入
      • 链表在指定位置的删除
        • 删除指定位置的结点
        • 删除指定位置之后的结点
      • 链表的销毁
    • 额外的知识:带头单向链表的初始化
  • 总结

前言

线性表的概述

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

链表的概述

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


内容

链表的结构

在这里插入图片描述
链表的结构跟⽕⻋⻋厢相似,并且每节⻋厢都是独⽴存在的;
那么思考一个问题:既然每个车厢都是独立的,那怎么样把他们链接起来?
想要把每节车厢都链起来,就是依赖每节车厢之间的接口,以方便链接;
而在链表中,就需要依赖指针域来将节点都链接起来;

在现实中数据结构的链表实现:
在这里插入图片描述

链表节点的定义

链表一共可以分为八种类型(在后面有详解),在这里我以结构最简单的一种来介绍链表的结构:这种结构叫做:无头单向不循环链表 简称 单向链表

但是链表中的每个节点怎么定义,为什么呢?
这里我们需要运用结构体来定义节点;
原因
1.结构体可以将不同类型的数据组合在一起,根据上述的分析:对于链表的结点,需要存储数据元素(int,float…)和下一个节点的地址;结构体的话就可以很好的将这两种不同类型的数据(数据域和指针域)封装在一起;
2.并且我们要了解:结构体可以比较方便的表示一种复杂的数据结构;而链表的结构也相对比较复杂,所以用结构体定义链表的结构在合适不过了;

//链表的实现
//单向链表的结点的定义
typedef int typeList;//将int类型重命名为typeList
typedef struct SListNode
{
    typeList data;//这样定义数据域的好处就是,可以随时更改任意类型如:char、short...
    struct SListNode* next;//定义指针域,存放下一个节点的地址,以便找到下一个节点
}SLNode;

链表的基本功能

最基础的功能 增 删 查 改

单向链表的初始化

链表开始的时候需要初始化链表,因为初始化能建立一个稳定的起始状态,就像盖房子之前需要先打好地基一样,以确保数据结构符合预期的初始性质;

//直接这样定义即可;
int main()
{
	SLNode*phead=NULL;
	return 0;
}

链表的插入操作

对于单向链表,插入操作可以分为四种:头插,尾插,在查找到的位置之后插入,在查找到的位置之前插入
在这里我先介绍:头插,尾插,其余在后续有介绍

头插操作

头插概述就是将一个新节点插入到单向链表的头部,使得新节点成为链表的新头部。

  • 头插操作是将新节点插入到链表的头部。在单链表中,头节点( phead )是访问链表的入口。
  • 头插操作需要改变头节点 phead 的指向。新插入的节点将成为新的头节点,原来的头节点将成为新节点的下一个节点。

询问:若将头插操作封装成一个函数,请问怎么传参呢?

  • 由于C语言函数参数是值传递,如果只传递一级指针 phead ,在函数内部对phead的修改只是修改了函数内部的一个副本。例如,在函数内 phead = newNode; newNode 是新插入的节点)这样的操作,只是改变了函数内部 phead 副本的指向,而不会改变函数外部真正的 phead
  • 传递二级指针 &phead ,函数接收的是头节点指针phead 的地址。这样在函数内部可以通过解引用二级指针来修改真正的头节点 phead 。比如, *pphead = newNode; pphead 是接收二级指针的参数),这种操作会改变函数外部的 phead 的值,使得新插入的节点成为链表的新头节点,从而实现了正确的头插操作。

但是头插要分情况讨论,若本来链表中就没有节点呢?
若有节点怎么办?若没有又该怎么样?
若有节点,就先让新的节点的next域指向头结点*pphead,再让头结点*pphead重新指向这个新的结点,
若没有节点,就直接让*pphead指向新的结点

//头插
void FrontSLNode(SLNode** pphead, typeList n)
{
	assert(pphead);//断言:就是当pphead为NULL,会自动报错,并结束程序;
	//创建新的节点,并将其指针域和数据域都要进行初始化;
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;
	}
	//初始化指针域和数据域
	newnode->data = n;
	newnode->next = NULL;
	//当链表为空,直接将新的结点插入链表头部
	if (*pphead == NULL)
	{
		*pphead = newnode;
		return;
	}
	//链表不为空,新插入的节点将成为新的头节点,原来的头节点将成为新节点的下一个节点。
	newnode->next = *pphead;
	*pphead = newnode;
	return 0;
}

尾插操作

尾插就是指在单链表的末尾插入一个新节点;

  • 在单链表中,当进行尾插操作时,需要考虑两种情况:一是链表为空时插入第一个节点,二是链表非空时插入新节点到尾部。
  • 首先你要知道,尾插如果当链表一开始为空时,要尾插的话,你就需要改变phead的指向,此时你就需要传入二级指针;(可以这样想:当链表非空时,这时尾插不需要改变phead指针的指向,此时传一级指针就可以)

为什么需要传二级指针:因为

  • 当链表为空时,头节点 phead 本身为 NULL 。如果只传一级指针,在函数内部修改 phead 的值(让它指向新插入的节点),这种修改在函数结束后不会影响到函数外部的 phead 变量。因为C语言中函数参数传递是值传递,对于指针变量也不例外。
  • 传递二级指针( &phead )就可以在函数内部真正地修改头节点 phead 本身的值。例如,在插入第一个节点时,函数内部可以通过 *pphead = newNode; (假设 pphead 是二级指针接收的参数, newNode 是新插入的节点)这样的操作来让外部的phead指向新节点。
  • 当链表非空时,通过二级指针也可以方便地遍历到链表的尾部进行插入操作,并且保证对链表结构的修改(如更新尾节点的 next 指针)能够在函数外部生效。
//尾插
void BackSLNode(SLNode** pphead, typeList n)
{
	assert(pphead);
	//创建新节点
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;
	}
	初始化指针域和数据域
	newnode->data = n;
	newnode->next = NULL;
	if (*pphead == NULL)
	{
		*pphead = newnode;
		return;
	}
	//链表不为空,找到尾巴
	SLNode* tem = *pphead;
	while (tem->next)
	{
		tem = tem->next;
	}
	//更新尾结点指向
	tem->next = newnode;
}

链表的删除操作

对于单向链表,删除操作可分为很多种比如:头删,尾删,删除指定位置的结点,删除指定位置之后的结点;接下来我来分别介绍:头删,尾删,后面的会在后续介绍;

当删除头结点时:

  • 当要删除头节点时,首先要判断链表是否为空。如果链表为空,那就没有节点可删除。
  • 若链表不为空,将头节点指针 phead 指向下一个节点( *pphead = *pphead->next ),此时改变了phead的头节点的指向,所以要传二级指针,然后释放原来头节点的内存空间,这样就完成了头节点的删除。

当不是删除头结点时:

  • 首先需要遍历链表找到要删除节点的前驱节点。从链表头开始,逐个节点检查,直到找到一个节点(设为 pre ),它的下一个节点( pre->next )是要删除的节点。
  • 然后将前驱节点 pre 的指针域跳过要删除的节点,即 pre->next = pre->next->next 。
  • 最后释放要删除节点的内存空间。
头删操作

头删:就是将第一个节点删掉,然后让第二个节点变成新的节点;

//头删
void FrontDelSLNode(SLNode** pphead)
{
	assert(pphead);
	assert(*pphead);//当链表为空时,最好断言一下,否则下面的(*pphead)->next 会报错
	//作为前驱节点,最后要删除
	SLNode* pre = *pphead;
	//只有一个节点的情况
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
		return;
	}
	//两个以上的结点的情况
	SLNode* ret = pre->next;
	*pphead = ret;
	free(pre);
	pre = NULL;
}
尾删操作

尾删:就是删除链表的最后一个节点,让倒数第二个节点的指针域指向NULL;

//尾删
void BackDelSLNode(SLNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	SLNode* pre = *pphead;
	//只有一个节点的情况
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
		return;
	}
	//两个以上的结点的情况,找到尾结点的前一个节点
	while (pre->next->next)
	{
		pre = pre->next;
	}
	//释放掉最后一个节点
	free(pre->next->next);
	pre->next->next = NULL;
	//再让倒数第二个节点的next域指向NULL
	pre->next = NULL;
}

链表的查找操作

查找:就是在单链表中寻找特定元素或节点,也就是说,传参的时候,可以传数据值或传地址进行查找;
注意:查找就是负责查找,并且返回那个节点而已,并不会改变phead头结点指针的方向,所以此时传一级指针即可;

按值查找:

  • 从链表的头节点开始,通过遍历链表逐个比较节点的数据域与目标值。
  • 设链表头节点为 phead ,可以使用一个指针 p = phead ,然后在循环中比较 p->data (假设 data 是存储数据的成员)和目标值。如果相等,就找到了目标节点;如果不相等,就将 p = p->next ,继续检查下一个节点,直到 p 为 NULL (表示遍历完链表未找到目标值)。

按址查找:

  • 同样从头节点开始,要查找第 n 个位置的节点。定义一个计数变量 count = 1 ,一个指针 p = phead 。
  • 当 count < n 且 p 不为 NULL 时,执行 p = p->next 和 count++ 操作。如果 count == n ,此时 p 指向的就是第 n 个节点;如果 p 为 NULL 且 count < n ,则表示链表长度小于 n ,不存在第 n 个节点。

这里我就偷个懒,就实现 按值查找 喽😊


//查找
SLNode* FindSLNode(SLNode* phead, typeList n)
{
	assert(phead);
	SLNode* p = phead;
	while (p)
	{
		if (p->data == n)
			return cour;
		p = p->next;
	}
	return NULL;
}

链表的在指定位置的插入

其实原理也是和头插,尾插原理相同;唯一需要理解的是什么叫在特定位置?这里我们就需要和之前的那个查找操作相结合了,先找到你需要插入的节点,再在对这个节点之前,节点之后进行插入新的节点;
注意,当你在在查找到的位置之前插入,就有可能会改变头结点phead指针的指向,需要传二级指针;但是当你在在查找到的位置之后插入,就不会改变头结点phead指针的指向,所以不需要传二级指针,一级指针就可以了;

在指定位置之前插入

此时传参不但需要查找到的那个节点,还需要将头结点传入进来,是为了遍历链表找到指定的那个节点

//在指定位置之前插入数据
void InsertSLNode(SLNode** pphead, SLNode* pos, typeList n)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);//此时还要对指定位置的结点断言
	//创建新节点,并进行初始化
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	newnode->data = n;
	newnode->next = NULL;

	//当在第一个节点之前插入
	if ((*pphead) == pos)
	{
		//就是头插,直接将头插的代码拿过来,Ctrl C V真的很快乐😍
		FrontSLNode(pphead, n);
		return;
	}

	//其他节点,找到指定位置之前的那个节点
	SLNode* tem = *pphead;
	while (tem->next != pos)
	{
		tem = tem->next;
	}
	//插进来
	tem->next = newnode;
	newnode->next = pos;
}

在指定位置之后插入

在指定位置之后插入,就不存在改变头节点phead的可能性,不需要遍历就能找到指定的那个节点;

//在指定位置之后插入数据
void InsertAfterSLNode(SLNode* pos, typeList n)
{
	assert(pos);
	//创建新节点,并进行初始化
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	newnode->data = n;
	newnode->next = pos->next;
	pos->next = newnode;
}

链表在指定位置的删除

其实和链表的在指定位置的插入的原理相同,先找到这个指定位置,在进行删除操作;
链表在指定位置的删除:可以删除指定位置的结点,删除指定位置之后的结点

删除指定位置的结点

删除指定位置的结点,需要传头结点用来遍历找到特定位置的结点,也有可能删除头结点phead,所以需要传二级指针;

进行特殊情况的判断:

  • 首先判断链表是否为空,如果为空,则无法进行删除操作,直接返回。
  • 如果指定位置是第 1 个节点,即删除头节点,这时候只需要将头指针指向头节点的下一个节点,并释放原来头节点的内存空间即可。

步骤:
1.查找要删除节点的前驱节点
2.再删除节点

//删除pos节点
void SLTErase(SLNode** pphead, SLNode* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);
	//当为第一个节点,进行头删
	if (pos == (*pphead))
	{
		//相当于进行头删操作
		FrontDelSLNode(pphead);
		return;
	}
	SLNode* tem = *pphead;
	while (tem->next != pos)
	{
		tem = tem->next;
	}
	tem->next = pos->next;

	free(pos);
	pos = NULL;
}
删除指定位置之后的结点

删除指定位置之后的结点,不存在改变头节点phead指针的可能性,所以传一级指针即可;

//删除pos之后的节点
void SLTEraseAfter(SLNode* pos)
{
	assert(pos);
	assert(pos->next);
	SLNode* tem = pos;

	pos = tem->next->next;
	free(tem->next);
	tem->next = NULL;
}

链表的销毁

链表的销毁就是指释放链表中所有节点占用的内存空间,使链表不再使用;
所以要改变头结点phead指针的指向,所以传二级指针

  • 首先需要一个指针ptr来遍历链表,接着使用一个临时指针next来保存当前节点 ptr的下一个节点的位置,然后释放当前节点ptr所占用的内存空间,最后将ptr指针更新为next;重复上述步骤,直到ptr==NULL,这意味着已经办理并释放了链表的所有节点;
//销毁链表
void SListDesTroy(SLNode** pphead)
{
	assert(pphead);
	assert(*pphead);

	SLNode* ptr = *pphead;
	while (ptr != NULL)
	{
		SLNode* next = ptr->next;
		free(ptr);
		ptr = NULL;
	}
	*pphead = NULL;
}

额外的知识:带头单向链表的初始化

链表开始的时候需要初始化链表,因为初始化能建立一个稳定的起始状态,就像盖房子之前需要先打好地基一样,以确保数据结构符合预期的初始性质;
当面对传参问题:
带头链表的初始化可以有两种形式:
1.无参数形式;2.参数形式

当为无参数形式 :因为链表的初始化主要是创建一个节点,并将指针域设置成NULL;
例如:这里的InitSLNode函数没有参数;

SLNode* InitSLNode() {
    SLNode* head = (SLNode*)malloc(sizeof(SLNode));
    if (head == NULL) {//这里的malloc向堆区中开辟空间可能失败,所以这边进行判断;
        return NULL;
    }
    //这里最好就将数据域也进行初始化一下,要不然你这个结点的数据域就是一个随机值
    head -> data = 0;
    //将指针域设为NULL,变成一个独立的节点
    head -> next = NULL;
    return head;
}
//无参数形式这样进行初始化
int main()
{
	SLNode*phead=InitSLNode();
	return 0;
}

当为参数形式 :其实传参数可以根据自身需求进行不同的设置:比如我这时传入的是一个整数为初始值来初始化链表的节点;
这里我们想的是把结构体变成一个链表的结点,所以我们直接传结构体的地址就可以了;我们就可以将这个结构体直接就变成节点;

例如:这里的InitSLNode函数有参数;

//初始化
void InitSLNode(SLNode* phead)
{
	//这里最好就将数据域也进行初始化一下,要不然你这个结点的数据域就是一个随机值
	phead->data = 0;
	//将指针域设为NULL,变成一个独立的节点
	phead->next = NULL;
}
//有参形式这样进行初始化
int main()
{
	SLNode head;
	InitSLNode(&head);

	return 0;
}

这里对于参数形式的传参问题:为什么传结构体的地址?
因为,只有传地址调用的时候可以直接修改结构体的成员(数据域和指针域);若你只是传结构体变量进来,不能改变结构体的成员(数据域和指针域)
如果还有不太懂的东西,可以去了解一下:传值调用和传址调用的区别;


总结


到了最后:感谢支持

------------对过程全力以赴,对结果淡然处之
也是对我自己讲的

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

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

相关文章

高校数字化校园中数据交换和共享平台的设计与实现(源码+定制+开发)校园数据整合平台、高校信息交换系统、校园数据整合平台、数字校园信息交换平台、校园数据集成管理

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

【Linux驱动开发】timer库下的jiffies时间戳和延时驱动编写

【Linux驱动开发】timer库下的jiffies时间戳和延时驱动编写 gitee地址&#xff1a; https://gitee.com/Mike_Zhou_Admin/Linux_Driver_Timestamp_Driver/更新以gitee为准 文章目录 timer库时间戳函数延时函数驱动代码应用测试附录&#xff1a;嵌入式Linux驱动开发基本步骤开发…

python安装了numpy却用不了且报错的解决方案和numpy的简单应用于图像处理

1.报错情况如下&#xff1a;Error importing numpy: 解决方法&#xff1a;降低python和numpy的版本&#xff0c;我一开始下载安装的都是最新版的python和numpy&#xff0c;后来降低了版本后就不报错且可正常使用了&#xff0c;这里给出我使用的版本作为参考&#xff08;记得卸…

Java链表及源码解析

文章目录 创建一个ILindkedList接口创建方法(模拟实现链表方法)创建MyLinkedList来实现接口的方法创建链表节点addFirst方法&#xff08;新增头部属性&#xff09;addLast方法&#xff08;新增到末尾一个属性&#xff09;remove方法&#xff08;删除指定属性&#xff09;addInd…

潮玩宇宙方块兽系统开发:可定制UI与多种游戏内嵌助力个性化体验

潮玩宇宙方块兽系统开发正在推动潮玩与游戏的融合&#xff0c;通过个性化的UI设计和多游戏内嵌模式&#xff0c;为用户带来了独一无二的体验。本文将从可定制UI、多游戏内嵌功能以及系统实现等方面入手&#xff0c;探讨如何构建一个极具吸引力的潮玩宇宙方块兽系统。 一、可定制…

C#属性 Property

属性Property不是变量。 它们是由名为访问器方法来实现的一种方法。 实例属性表示的是实例的某个数据&#xff0c;通过这个数据反映实例当前的状态 静态属性表示的是类型的某个数据&#xff0c;通过这个数据反映类型当前的状态 意义&#xff1a; 防止恶意赋值(通过属性间接访问…

第八篇: 通过使用Google BigQuery进行数据批量和自动化处理

使用Python进行Google BigQuery数据批量和自动化处理 在大数据分析的日常工作中&#xff0c;定期更新、查询和处理数据是一项必不可少的任务。Google BigQuery结合Python脚本&#xff0c;可大幅简化这一过程。本文将介绍如何通过Python自动查询和更新BigQuery中的降水量数据&a…

AI - 人工智能;Ollama大模型工具;Java之SpringAI(三)

AI - 人工智能&#xff1b;Java之SpringAI&#xff08;一&#xff09; AI - 人工智能&#xff1b;Java之SpringAI&#xff08;二&#xff09; 一、Ollama 官网&#xff1a;https://ollama.com/ Ollama是一个大模型部署运行工具&#xff0c;在该工具里面可以部署运行各种大模型…

MySQL_数据类型建表

复习&#xff1a; 我们昨天学习的知识都忘了嘛&#xff1f;如果忘了也不要担心&#xff0c;我来带大家来复习一遍吧&#xff01;&#xff01;&#xff01; 1.查看所有数据库 show databases;2.创建属于自己的数据库 create database 数据库名; 检查自己创建的数据库是…

零基础入门进程间通信:task 1(匿名管道与vscode使用)

目录 引言 VSCODE使用 进程间通信正题 基础背景 进程间通信分类 匿名管道 理解匿名管道 代码实现 匿名管道的特性 管道的四种情况 应用场景 引言 在当今的计算机技术领域&#xff0c;操作系统作为计算机系统的核心组件&#xff0c;承担着资源管理、任务调度和进程管…

Vue 3 的 全局状态管理

1.思路梳理 工厂仓拣货信息&#xff1a;Factory Picking Info (FPI)工厂仓调度信息&#xff1a;Factory Scheduling Info (FSI)DC 收货信息&#xff1a;DC Receiving Info (DCRI)上架信息&#xff1a;Shelving Info (SI)盘点信息&#xff1a;Inventory Count Info (ICI)移位信…

Win系统通过命令行查看笔记本电池损耗/寿命/健康

在 Windows 10/11 系统中&#xff0c;可以通过指令查看笔记本电池的寿命情况&#xff0c;方法如下&#xff1a; 0&#xff0c;打开cmd/终端 键盘快捷键&#xff1a;Win R&#xff0c;然后输入cmd&#xff0c;点击【确定】 1&#xff0c;执行命令 在命令行中输入下面指令并按…

【DM系列】DM 集成 JDBC 开发指南

前言 数据库访问是数据库应用系统中非常重要的组成部分&#xff0c;DM 作为一个通用数据库管理系统&#xff0c;提供了多种数据库访问接口&#xff0c;包括 ODBC、JDBC、DPI 等方式。本开发指南详细介绍了 DM 的各种访问接口、相应开发环境的配置、以及一些开发用例。本指南的主…

【客观理性深入讨论国产中间件及数据库-科创基础软件】

随着国产化的进程&#xff0c;越来越多的国企央企开始要求软件产品匹配过程化的要求&#xff0c; 最近有一家银行保险的科技公司对行为验证码产品就要求匹配国产中间件&#xff0c; 于是开始了解国产中间件都有哪些厂家 一&#xff1a;国产中间件主要产品及厂商 1 东方通&…

python opencv3

三、图像预处理2 1、图像滤波 为图像滤波通过滤波器得到另一个图像。也就是加深图像之间的间隙&#xff0c;增强视觉效果&#xff1b;也可以模糊化间隙&#xff0c;造成图像的噪点被抹平。 2、卷积核 在深度学习中&#xff0c;卷积核越大&#xff0c;看到的信息越多&#xff0…

数据库管理-第258期 23ai:Oracle Data Redaction(20241104)

数据库管理258期 2024-11-04 数据库管理-第258期 23ai&#xff1a;Oracle Data Redaction&#xff08;20241104&#xff09;1 简介2 应用场景与有点3 多租户环境4 特性与能力4.1 全数据编校4.2 部分编校4.3 正则表达式编校4.4 随机编校4.5 空值编校4.6 无编校4.7 不同数据类型上…

Kettle——CSV文件转换成excel文件输出

1.点击—文件—新建—转换 拖入两个组件&#xff1a; 按shift&#xff0b;鼠标左击建立连接&#xff0c;并点击主输出步骤&#xff0c; 点击CSV文件输入&#xff0c;选择浏览的csv文件&#xff0c;然后点击确定 同样&#xff0c;Excel也同上&#xff0c;只是要删除这个xls 并…

【数据集】【YOLO】【目标检测】火情、烟雾、火灾检测数据集 9848 张,YOLO火灾检测算法实战训练教程!

数据集介绍 【数据集】火情、烟火、火灾检测数据集 9848 张&#xff0c;目标检测&#xff0c;包含YOLO/VOC格式标注。 数据集中包含2种分类&#xff1a;{0: Fire, 1: Smoke}&#xff0c;分别是‘火焰’和‘烟雾’。 数据集来自国内外图片网站和视频截图&#xff1b; 可用于…

Python酷库之旅-第三方库Pandas(202)

目录 一、用法精讲 941、pandas.CategoricalIndex.set_categories方法 941-1、语法 941-2、参数 941-3、功能 941-4、返回值 941-5、说明 941-6、用法 941-6-1、数据准备 941-6-2、代码示例 941-6-3、结果输出 942、pandas.CategoricalIndex.as_ordered方法 942-1…

docker 拉取MySQL8.0镜像以及安装

目录 一、docker安装MySQL镜像 搜索images 拉取MySQL镜像 二、数据挂载 在/root/mysql/conf中创建 *.cnf 文件 创建容器,将数据,日志,配置文件映射到本机 检查MySQL是否启动成功&#xff1a; 三、DBeaver数据库连接 问题一、Public Key Retrieval is not allowed 问题…