单链表的增删改查(数据结构)

之前我们学习了动态顺序表,今天我们来讲一讲单链表是如何进行增删改查的

一、单链表

1.1、单链表概念

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

1.2、链表与顺序表的区别:

链表不像顺序一样,空间给少了不够,给多了浪费,链表是你需要多少空间,就申请多少空间。

链表中一个结点就是一个数据,结点中必须包含两个数据,一个是我们需要存放的数据,另外一个就是下一个数据的地址。

每存放一个数据,我们就需要申请一个结点,直到最后一个结点指向的地址为NULL

淡季时⻋次的⻋厢会相应减少,旺季时⻋次的⻋厢会额外增加⼏节。只需要将⽕⻋⾥的某节⻋厢去掉/加上,不会影响其他⻋厢,每节⻋厢都是独⽴存在的。

在链表⾥,每节“⻋厢”是什么样的呢?

1.3、链表的打印

为什么提前讲链表是如何打印的,而不是先讲插入删除,初始化,这是因为比较好理解,这个理解的,接下来在看增删查改理解就会快了。

当pcur为头结点时,需要打印第一个结点的数据,第一个结点的数据为pcur这个结构体中的data数据,所有我们需要获取data数据进行打印。当需要打印第二个结点里data数据时候,pcur需要往前走一个结点,走到的结点是pcur结构体中存放的下一个结点的地址进行解引用,然将解引用得到的数据赋值给pcur,此时pcur就往前前进了一位。直到pcur等于NULL才停止循环。

二、增删改查

在这一部分,只写node.c中增删改查实现的函数代码,总代码可在最后进行查看。

2.1、结点的创建(结构体)

node.h

typedef int SLTDataType;     //将int类型定义为SLTDataType,方便修改数据类型

typedef struct SListNode {
	SLTDataType data;       
	struct SListNode* next;    //指向的下一个结点
}SLTNode;

struct SListNode* next;这段代码是创建一个为类型大小为struct SListNode指针的next,next指向下一个结点,为什么不是这样写struct SListNode next;  如果这样的话,每个结点都包含一个完整的 SListNode ,那么造成了巨大的空间浪费,在进行移动的时候,占用非常大的计算。

这样写struct SListNode* next; 那么在进行两个结点连接时,next可以存储另外一个结点的地址,可以快速找到下一个数据

2.2、创建头结点

test.c

int main()
{
	SLTNode* plist = NULL;   //创建头结点
    return 0;
}

为什么需要使用SLTNode* plist = NULL; 那是因为plist是一个指向SLTNode的一个指针,我们可以用这个来指向链表的头结点,并且在链表的动态中进行改变他的值。

SLTNode plist = NULL;这样写的意思是plist的类型是SLTNode并且等于NULL,这是一个极其错误的写法。

所以如果我们想要动态的使用结点,就需要创建一个头结点指向SLTNode,这样就可以使用结构体中的数据。

那为什么不这样写 SLTNode plist;然后对结构体进行初始化。那是因为,我们所创建的不是一个顺序表,我们需要记录下一个空间存入的数据与该空间的下一个结点。

2.3、尾插

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	//pphead --> &plist
	// *pphead --> plist
	//申请新节点
	SLTNode* newnode = SLTBuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾结点
		SLTNode* pcur = *pphead;
		while (pcur->next)
		{
			pcur = pcur->next;
		}
		//pcur  newnode
		pcur->next = newnode;
	}
}

void SLTPushBack(SLTNode** pphead, SLTDataType x)注意这里使用了二级指针。

为什么要使用二级指针?

由于我们需要修改头指针的内容,而传递指针本质上是值传递,所以我们需要通过二级指针来直接操作头指针的地址,才能真正修改头指针的值。

在顺序表中,由于你是在固定大小的数组上操作,数组的大小和起始地址是固定的,不需要动态分配和改变,所以不需要二级指针。

在链表中,如果直接将创建的头结点的值传入函数中,那么就相当于传值调用,在程序运行完以后,栈帧也会消失,所以需要取地址进行函数调用,这样头结点内容就被修改了。

2.4、申请新结点

//申请新节点
SLTNode* SLTBuyNode(SLTDataType x)
{
	SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
	if (node == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	node->data = x;
	node->next = NULL;

	return node;
}

在链表中使用malloc来扩容,而非增容(realloc),当创建好一个新结点时,把数据(x)存放到新结点中的data里面,新结点指向的下一个指针next先置为空,在接下来增删改查时候再做修改。

2.5、尾插

//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);                     //判断pphead是否为NULL
	//申请新节点
	SLTNode* newnode = SLTBuyNode(x);   //创建一个新结点
	if (*pphead == NULL)
	{
		*pphead = newnode;      //如果*pphead是已经初始化为NULL,那么直接将新结点等于头结点
	}
	else
	{
		//找尾结点
		SLTNode* pcur = *pphead;     //创建一个新指针等于pphead的
		while (pcur->next)           //pcur->next不等于NULL就一直运行
		{
			pcur = pcur->next;       //一直将pcur往后遍历,直到pcur->next等于NULL
		}
		pcur->next = newnode;        //将原链表pcur->next指向NULL的结点指针指向新的结点
	}
}

为什么我们不适用*pphead来遍历链表,因为我们在打印链表的时候,需要从头结点开始遍历,如果使用*pphead来遍历的话,此时*pphead就走到了链表的末尾,在打印链表的时候就无法打印,所以就需要创建一个指针等于*pphead,借助新创建的指针来进行链表的修改,当新指针走到了链表的末尾,*pphead还是在链表的头结点。

2.6、头插

//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);

	SLTNode* newnode = SLTBuyNode(x);  //创建新结点
	newnode->next = *pphead;           //将新创建的头结点中指向下一个结点的指针直接指向*pphead
	*pphead = newnode;                 //将*pphead等于newnode,此时头结点还是*pphead
} 

2.7、尾删

//尾删
void SLTPopBack(SLTNode** pphead)
{
	//链表为空:不可以删除
	assert(pphead && *pphead);
	//处理只有一个结点的情况:要删除的就是头结点	
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		//找 prev ptail
		SLTNode* ptail = *pphead;
		SLTNode* prev = NULL;
		while (ptail->next)
		{
			prev = ptail;
			ptail = ptail->next;
		}
		prev->next = NULL;
		free(ptail);
		ptail = NULL;
	}
}

............................................

第四个图中,newtail->next = NULL,此时循环停止,将prev->next置为NULL,此时prev就是最后一个结点,接着将不需要的结点newtail中的空间给释放掉.

2.8、头删

//头删
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);

	SLTNode* next = (*pphead)->next;//记录头结点指向的下一个结点
	free(*pphead);   //释放头结点
	*pphead = next;   //将*pphead等于刚刚记录下来的结点,此时就完成了头结点的删除操作
}

2.9、查找链表中的数据

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	assert(phead);
	SLTNode* pcur = phead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	//没有找到
	return NULL;
}

当链表中存放的数据等于x时,返回pcur,否则就返回NULL

2.10、在指定位置之前插⼊数据

//在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);        //查找的数据不能为空,需要是一个有效的数

	if (pos == *pphead)
	{
		SLTPushFront(pphead, x);  如果pos等于头结点,那么直接进行头插
	}
	else
	{
		SLTNode* newnode = SLTBuyNode(x);
		//找prev :pos的前一个结点
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		newnode->next = pos;
		prev->next = newnode;
	}
}

例如需要查找的数是pos=3,prev->next中的地址解引用得到的结果就是3,所以prev停止pos结点之前的一个结点。

将newnode->next指针指向pos,接着将prev->next指向newnode。这样就完成了链表的插入。

2.11、在指定位置之后插⼊数据

//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

例如pos=2,那么图就是这这样的

2.12、删除pos结点

//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);

	//头删
	if (pos == *pphead)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

2.13、删除pos之后的结点

//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);
	SLTNode* del = pos->next;
	pos->next = pos->next->next;
	free(del);
	del = NULL;
}

2.14、销毁链表

//销毁链表
void SListDestroy(SLTNode** pphead)
{
	assert(pphead && *pphead);

	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;    //遍历每一个结点,将每一个结点空间进行释放
		free(pcur);
		pcur = next;                   //将释放后的结点,等于下一个结点,直到pcur=NULL
	}
	*pphead = NULL;                    //释放完全部结点以后,将*pphead也置为NULL
}

三、全部源代码

node.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

//定义链表(结点)的结构
typedef int SLTDataType;

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

void SLTPrint(SLTNode* phead);

//插入
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);

//删除
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);

//在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);

//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SListDestroy(SLTNode** pphead);
test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "node.h"


void SListTest01()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPrint(plist);//1->2->3->4->NULL

	//SLTPushBack(NULL, 3);
	//SLTPushFront(&plist, 1);
	//SLTPushFront(&plist, 2);
	//SLTPushFront(&plist, 3);
	//SLTPushFront(&plist, 4);
	//SLTPrint(plist); //4->3->2->1->NULL

	//SLTPopBack(&plist);
	//SLTPrint(plist);
	//SLTPopBack(&plist);
	//SLTPrint(plist);
	//SLTPopBack(&plist);
	//SLTPrint(plist);
	//SLTPopBack(&plist);
	//SLTPrint(plist);
	//SLTPopBack(&plist);
	//SLTPrint(plist);
	//
	//SLTPopFront(&plist);
	//SLTPrint(plist);
	//SLTPopFront(&plist);
	//SLTPrint(plist);
	//SLTPopFront(&plist);
	//SLTPrint(plist);
	//SLTPopFront(&plist);
	//SLTPrint(plist);
	//SLTPopFront(&plist);
	//SLTPrint(plist);

	//SLTNode* find = SLTFind(plist, 4);
	//if (find == NULL)
	//{
	//	printf("未找到!\n");
	//}
	//else
	//{
	//	printf("找到了!\n");
	//}
	//SLTInsert(&plist, find, 11);//4->3->2->11->1->NULL
	//SLTInsertAfter(find, 11);
	//SLTPrint(plist);1->11->2->3->4->NULL

	//SLTErase(&plist, find);// 1->2->3->NULL
	//SLTEraseAfter(find);

	//SLTPrint(plist);
	SListDestroy(&plist);
	SLTPrint(plist);
}

int main()
{
	SListTest01();
	return 0;
}
node.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "node.h"

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

//申请新节点
SLTNode* SLTBuyNode(SLTDataType x)
{
	SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
	if (node == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	node->data = x;
	node->next = NULL;

	return node;
}

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	//pphead --> &plist
	// *pphead --> plist
	//申请新节点
	SLTNode* newnode = SLTBuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾结点
		SLTNode* pcur = *pphead;
		while (pcur->next)
		{
			pcur = pcur->next;
		}
		//pcur  newnode
		pcur->next = newnode;
	}
}
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);

	SLTNode* newnode = SLTBuyNode(x);
	//newnode *pphead
	newnode->next = *pphead;
	*pphead = newnode;
}
void SLTPopBack(SLTNode** pphead)
{
	//链表为空:不可以删除
	assert(pphead && *pphead);
	//处理只有一个结点的情况:要删除的就是头结点	
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		//找 prev ptail
		SLTNode* ptail = *pphead;
		SLTNode* prev = NULL;
		while (ptail->next)
		{
			prev = ptail;
			ptail = ptail->next;
		}
		prev->next = NULL;
		free(ptail);
		ptail = NULL;
	}
}
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);

	SLTNode* next = (*pphead)->next;
	//*pphead --> next
	free(*pphead);
	*pphead = next;
}
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	assert(phead);
	SLTNode* pcur = phead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	//没有找到
	return NULL;
}
//在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);

	if (pos == *pphead)
	{
		SLTPushFront(pphead, x);
	}
	else
	{
		SLTNode* newnode = SLTBuyNode(x);
		//找prev :pos的前一个结点
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		//prev newnode --> pos
		newnode->next = pos;
		prev->next = newnode;
	}
}
//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	//pos newnode --> pos->next
	newnode->next = pos->next;
	pos->next = newnode;
}
//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);

	//头删
	if (pos == *pphead)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		//prev pos pos->next
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);

	//pos pos->next pos->next->next
	SLTNode* del = pos->next;
	pos->next = pos->next->next;
	free(del);
	del = NULL;
}
//销毁链表
void SListDestroy(SLTNode** pphead)
{
	assert(pphead && *pphead);

	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

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

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

相关文章

python的几个基本数据类型及其相关操作(字符串str,元组tuple,列表list,字典dict)

一、str及其相关操作 1、字符串的基本方法 字符串的索引、获取字符串长度、利用index获取索引位置&#xff0c;统计某字符在字符串中出现的次数。用法如下方代码。 python的变量在创建时不需要声明其数据类型&#xff0c;他会自动识别变量后的数据类型&#xff0c;所以创建一…

(undone) 阅读 MapReduce 论文笔记

参考&#xff1a;https://pdos.csail.mit.edu/6.824/papers/mapreduce.pdf 摘要&#xff1a;简单介绍了 MapReduce 是在大型分布式系统上工作的 Introduction 的内容总结&#xff1a; 1.介绍背景&#xff1a;为什么我们需要分布式系统&#xff1f;MapReduce 的意义是哪些 2.简…

运动耳机哪个牌子的好?5大质量不凡的运动耳机测评力荐!

在快节奏的生活中&#xff0c;无论是晨跑、健身还是户外探险&#xff0c;音乐都成了许多人不可或缺的陪伴。运动耳机&#xff0c;作为一种专为运动场景设计的音频设备&#xff0c;旨在提供高质量音频体验的同时&#xff0c;保证佩戴的舒适度和运动的安全性。 &#xff08;上图为…

YOLOv11改进 | 主干篇 | YOLOv11引入MobileNetV4

1. MobileNetV4介绍 1.1 摘要&#xff1a; 我们推出了最新一代的 MobileNet&#xff0c;称为 MobileNetV4 (MNv4)&#xff0c;具有适用于移动设备的通用高效架构设计。 在其核心&#xff0c;我们引入了通用倒瓶颈&#xff08;UIB&#xff09;搜索块&#xff0c;这是一种统一且…

小川科技携手阿里云数据库MongoDB:数据赋能企业构建年轻娱乐生态

随着信息技术的飞速发展&#xff0c;企业在处理海量数据时所面临的挑战日益严峻。特别是在年轻娱乐领域&#xff0c;用户行为的多样性和数据量的激增对数据存储与分析技术提出了更高的要求。在此背景下&#xff0c;小川凭借其前瞻性的技术视野&#xff0c;选择了MongoDB作为其数…

AlmaLinux 9 安装mysql8.0.38

文件下载 https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.39-linux-glibc2.12-x86_64.tar 选择合适系统版本 下载后解压 tar -xvf mysql-8.0.39-linux-glibc2.12-x86_64.tar解压后里面有三个文件夹 使用mysql-8.0.39-linux-glibc2.12-x86_64.tar.xz即可&#xff0c…

Redis中String类型的常用命令(append,getrenge,setrange等命令)

Redis----String命令 前言.常见的String存储类型. 常见命令1. set 命令2. get 命令3. mget命令与mset命令4. setnx命令5. setex与psetex命令6. incr与incrby与incrbyfloat命令7. decr与decrby命令8. append命令9. getrange和setrange命令10. strlen命令. 前言. 常见的String存…

【Kubernetes】常见面试题汇总(四十五)

目录 102.使用 Kubernetes 时可以采取的最佳安全措施是什么&#xff1f; 103.什么是联合集群&#xff1f; 特别说明&#xff1a; 题目 1-68 属于【Kubernetes】的常规概念题&#xff0c;即 “ 汇总&#xff08;一&#xff09;~&#xff08;二十二&#xff09;” 。 题目…

高中教辅汇总【35GB】

文章目录 一、资源概览二、资源亮点三、获取方式 一、资源概览 这份教辅资源汇总&#xff0c;精心搜集了高中各学科的海量教辅资料&#xff0c;总容量高达35GB&#xff0c;覆盖了语文、数学、英语、物理、化学、生物、历史、地理、政治等所有必修及选修科目。从基础知识点到难…

插槽slot在vue中的使用

介绍 在 Vue.js 中&#xff0c;插槽&#xff08;slot&#xff09;是一种用于实现组件内容分发的功能。通过插槽&#xff0c;可以让父组件在使用子组件时自定义子组件内部的内容。插槽提供了一种灵活的方式来组合和复用组件。 项目中有很多地方需要调用一个组件&#xff0c;比…

【H2O2|全栈】关于CSS(9)CSS3扩充了哪些新鲜的东西?(二)

目录 CSS3入门 前言 准备工作 伪元素补充 :before :after 文本溢出属性 转换效果 预告和回顾 后话 CSS3入门 前言 本系列博客主要介绍CSS相关的知识点。 这一期主要介绍以下几个CSS3的知识点&#xff1a; 伪元素补充文本溢出属性转换 没有基础的朋友&#xff…

【Docker】配置文件

问题 学习Docker期间会涉及到docker的很多配置文件&#xff0c;可能会涉及到的会有&#xff1a; /usr/lib/systemd/system/docker.service 【docker用于被systemd管理的配置文件】 /etc/systemd/system/docker.service.d【覆盖配置文件的存放处】 /etc/systemd/system/mul…

网页前端开发之Javascript入门篇(4/9):循环控制

Javascript循环控制 什么是循环控制&#xff1f; 答&#xff1a;其概念跟 Python教程 介绍的一样&#xff0c;只是语法上有所变化。 参考流程图如下&#xff1a; 其对应语法&#xff1a; var i 0; // 设置起始值 var minutes 15; // 设置结束值&#xff08;15分钟…

VMware Aria Operations for Networks 6.13 发布,新增功能概览

VMware Aria Operations for Networks 6.13 - 网络和应用监控工具 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-aria-operations-for-networks/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org VMware Aria Oper…

Golang | Leetcode Golang题解之第440题字典序的第K小数字

题目&#xff1a; 题解&#xff1a; func getSteps(cur, n int) (steps int) {first, last : cur, curfor first < n {steps min(last, n) - first 1first * 10last last*10 9}return }func findKthNumber(n, k int) int {cur : 1k--for k > 0 {steps : getSteps(cu…

Llama 系列简介与 Llama3 预训练模型推理

1. Llama 系列简介 1.1 Llama1 由 Meta AI 发布&#xff0c;包含 7B、13B、33B 和 65B 四种参数规模的开源基座语言模型 数据集&#xff1a;模型训练数据集使用的都是开源的数据集&#xff0c;总共 1.4T token 模型结构&#xff1a;原始的 Transformer 由编码器&#xff08…

C++入门基础 (超详解)

文章目录 前言1. C关键字2. C的第一个程序3. 命名空间3.1 namespace的定义3.2 命名空间的嵌套3.3 命名空间使用3.4 查找优先级总结 4. C输入和输出4.1 标准输入输出 (iostream库)4.2 文件输入输出 (fstream库)4.3 字符串流 (sstream库)4.4 C格式化输出4.5 std::endl和\n的区别 …

56 门控循环单元(GRU)_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 文章目录 系列文章目录门控循环单元&#xff08;GRU&#xff09;门控隐状态重置门和更新门候选隐状态隐状态 从零开始实现初始化模型参数定义模型训练与预测 简洁实现小结练习 门控循环单元&#xff08;GRU&#xff09; 之前我们讨论了如何在循环神经网络中计算梯…

生信初学者教程(二十三):REF+SVM筛选候选标记物

文章目录 介绍加载R包导入数据准备数据机器学习特征筛选数据分割基础模型Recursive Feature Elimination特征筛选调参最终分类模型测试集验证标记基因输出结果总结介绍 采用了REF(Recursive Feature Elimination) 结合 SVM(Support Vector Machine) 的方法,对差异基因(参…

探索未来:hbmqtt,Python中的AI驱动MQTT

文章目录 **探索未来&#xff1a;hbmqtt&#xff0c;Python中的AI驱动MQTT**1. 背景介绍2. hbmqtt是什么&#xff1f;3. 安装hbmqtt4. 简单的库函数使用方法4.1 连接到MQTT服务器4.2 发布消息4.3 订阅主题4.4 接收消息4.5 断开连接 5. 应用场景示例5.1 智能家居控制5.2 环境监测…