C——单链表

一.前言

我们在前面已经了解了链表中的双向链表,而我们在介绍链表分类的时候就说过常用的链表只有两种——双向带头循环链表和单向不带头不循环链表。下来我来介绍另一种常用的链表——单向不带头不循环链表也叫做单链表。不清楚链表分类的以及不了解双向链表的可以看我之前的博客C——双向链表-CSDN博客。

二.单链表的结构

我们已经了解了单链表的全称叫做单向不带头不循环链表,我们怎么理解这链表前面的修饰词呢?其实,单链表就像是一节节连接起来的火车车厢一样。但是这节车厢只可以向后走,并且火车的车头和车尾是不能连接在一起的。

我们照着这张图再来分析单链表的结构:单向的意思就是只能沿着D->A->C->E->Z->X->W这个方向走,那不带头的意思就是没有头节点即哨兵位,我们在讲双向链表的时候提到了哨兵位,他只是一个没有有效数据的头节点。不循环的意思我们也可以对照这双向链表来看,双向链表的尾节点可以找到头节点,头节点也可以找到尾节点,这就是循环;而单链表的尾节点指向的不是头节点而是NULL,头节点也不能找到尾节点。

那么单链表的节点到底是什么样的呢?

 我们再来与双向链表的节点进行对比一下:

 单链表节点只有两个成员,分别是数据和指向下一个节点的指针,而双向链表的节点有三个成员,分别是数据,指向前一个节点的指针和指向下一个节点的指针。

综上所述:单链表是一个只能指向下一个节点的链表,而且没有头节点,并且是不循环的。 

三.实现单链表

与双向链表的实现相似,实现单链表也需要很多的函数及头文件,所以我们将所有的函数声明和头文件都放到singlelist.h中,函数的实现都放入singlelist.c中。

3.1单链表节点的结构

//链表节点
struct SingleList
{int val;struct SingleList* next;
};

我们这样写虽然可以完成链表的基本结构,但是难道我们链表只能存储整形嘛?如果我们创建了很多个整型节点,但是有一天我们想利用链表存储字符型或者浮点型数据,那么我们就得一个一个的去修改,费时费力;还有就是这个节点的名称很长,在后面的代码中会重复出现,也很浪费时间。所以我们可以利用typedef关键词,对节点重命名,以及对int重命名。

//节点数据类型
typedef int SingleListdatatype;//链表节点
typedef struct SingleListNode
{SingleListdatatype val;struct SingleListNode* next;
}SLNode;

这样我们在多次使用该类型的时候就不需要再写那么长一串了,以后再修改储存的数据类型的时候也就不用一个一个节点的修改了,只需将定义的节点数据类型中的数据类型修改即可。

3.2单链表节点的创建

 我们在创建节点的时候实际上就是创建一个结构体变量,我们可以利用动态内存管理为我们的每一个节点动态开辟一块内存空间。

//节点的创建
SLNode* BuyNode(SingleListdatatype x)
{SLNode* node = (SLNode*)malloc(sizeof(SLNode));if (node == NULL){perror("malloc");exit(-1);}node->val = x;node->next = NULL;return node;
}

 我们来测试一下节点创建函数是否正确:

我们利用此测试代码来调试发现,我们创建了4个节点,通过调试也确实观察到了这四个节点,并且它们储蓄的数据就是我们传的数据,指向下一个节点的指针也是NULL。与我们想要实现的结果相同。 

那我们现在将这四个节点连接起来:

void test1()
{SLNode* plist = NULL;SLNode* plist1 = BuyNode(1);SLNode* plist2 = BuyNode(2);SLNode* plist3 = BuyNode(3);SLNode* plist4 = BuyNode(4);plist1->next = plist2;plist2->next = plist3;plist3->next = plist4;
}int main()
{test1();return 0;
}

我们再来调试一下: 

我们看到将所有节点连接起来之后,我们就可以利用第一个节点找到后面的节点。 

3.3链表的打印

//链表的打印
void SLNodePrint(SLNode* plist)
{while (plist)//plist != NULL{printf("%d->", plist->val);plist = plist->next;}printf("NULL\n");
}

 我们再来测试一下该函数:

也是没有出现问题,说明我们链表的打印已经完成了。 

3.4尾插

我们现在创建的节点需要我们手动将其连接起来,我们可以利用尾插方法,将新创建的节点直接连接到前一个节点上。 

//尾插
void SLNodepushback(SLNode** plist, SingleListdatatype x)
{assert(plist);SLNode* newnode = BuyNode(x);//新节点if (*plist == NULL){//空链表*plist = newnode;}else{//非空链表//遍历原链表,找到尾节点SLNode* pcur = *plist;while (pcur->next)//(*plist)->next != NULL{pcur = pcur->next;}pcur->next = newnode;}
}

我们测试下尾插方法:

我们尾插了四次,通过打印方法,将链表打印出来,说明我们的尾插方法也没有问题。但是有些人可能会有疑问:为什么这里传的是二级指针 而不传一级指针?

因为我们是通过一个结构体类型的指针来维护我们的链表,而我们传一级指针的话相当于值传递,而对于值传递来说,形参改变是不影响实参的。简单来说,就说你在函数内部将形参节点的next指针改变了,但是却不影响实参的值。达不到我们的目的。所以要想形参的改变影响实参的话,我们就得地址传递,就得传一级指针的地址,也就是二级指针

3.5头插 

头插从名字就可以听出来是把一个新节点插到链表的头部,我们利用图来分析如何进行头插:

//头插
void SLpushfront(SLNode** plist, SingleListdatatype x)
{assert(plist);SLNode* newnode = BuyNode(x);newnode->next = *plist;*plist = newnode;
}

我们测试头插方法:

根据打印结果来看,头插的方法也没有任何问题。 

3.6尾删

尾删就是删除链表中的尾节点,那么前提就是该链表不能为空,如果都是空链表了,怎么执行删除操作呢?我们画图来分析一下:

//尾删
void SLpopback(SLNode** plist)
{assert(plist && *plist);//*plist != NULL防止链表为空SLNode* pcur = *plist;//记录倒数第二个节点SLNode* del = *plist;//记录尾节点while (del->next){pcur = del;del = del->next;}pcur->next = NULL;free(del);del = NULL;
}

我们测试之后发现出了错误,这是为什么呢?

我们进行四次尾删,最后一次删除会将1删除掉,此时应该只打印NULL,这里打印了随机值。这是为什么呢? 

//尾删
void SLpopback(SLNode** plist)
{assert(plist && *plist);//*plist != NULL防止链表为空if (!((*plist)->next))//(*plist)->next == NULL{//链表只有一个节点free(*plist);*plist = NULL;}else{//链表有多个节点SLNode* pcur = *plist;//记录倒数第二个节点SLNode* del = *plist;//记录尾节点while (del->next){pcur = del;del = del->next;}pcur->next = NULL;free(del);del = NULL;}
}

我们现在再来测试一下修改后的尾删代码: 

我们看到,现在就没有问题了。

3.7头删

我们实现了尾删,现在来实现头删。头删的前提与尾删的前提相同:链表不能为空。头删实际上就是删除链表中的第一个节点,我们画图分析如何实现头删:

//头删
void SLpopfront(SLNode** plist)
{assert(plist && *plist);//链表不能为空SLNode* next = (*plist)->next;free(*plist);*plist = next;
}

我们来测试头删方法:

没有问题~ 

那如果我们多删一次呢?

我们看到,第五次删除的时候这时候已经是空链表了,我们再看提示的错误信息,说是断言出错了,这就说明了链表为空了。 

3.8查找数据

查找数据非常简单,我们只需要遍历链表,将要查找的数据与链表中的数据进行对比,如果找到了则返回该数据对应节点的地址;如果没找到则返回NULL。

//查找
SLNode* SLFind(SLNode* plist, SingleListdatatype x)
{assert(plist);SLNode* pcur = plist;while (pcur){if (pcur->val == x){return pcur;}pcur = pcur->next;}return NULL;
}

测试查找:

结果没毛病,老铁OK了! 

3.9在指定位置之前插入数据

我们要在指定位置的前面插入数据,所以我们必须得找到该指定位置,这就需要调用我们的查找方法了。我们来画图分析一下:

//在指定位置之前插入数据
void SLposfront(SLNode** plist, SLNode* pos, SingleListdatatype x)
{assert(plist && *plist);//链表不能为空,否则找不到指定位置if (pos == *plist){//pos节点就是第一个节点//调用头插方法SLpushfront(plist,x);}else{//pos节点不是第一个节点SLNode* newnode = BuyNode(x);SLNode* pcur = *plist;SLNode* prev = *plist;while (pcur){if (pcur->val == pos->val){break;}prev = pcur;pcur = pcur->next;}// prev newnode pcur/posprev->next = newnode;newnode->next = pcur;}
}

 我们测试该代码:

我们测试了三种位置的插入,无论是第一个节点之前还是中间节点之前或者是尾节点之前都没有问题。

3.10在指定位置之后插入数据

在指定位置之后插入数据与在指定位置之前插入数据有相同点但也有不同点,我们继续来画图分析:

//在指定位置之后插入数据
void SLposback(SLNode* pos, SingleListdatatype x)
{assert(pos);SLNode* newnode = BuyNode(x);//pos newnode pos->nextnewnode->next = pos->next;pos->next = newnode;
}

我们对其进行测试来看: 

3.11删除pos节点

我们现在来删除指定节点,前提肯定是链表不能为空。我们接着来画图分析:

//删除pos节点
void slpoppos(SLNode** plist, SLNode* pos)
{assert(plist && *plist);assert(pos);if (*plist == pos){//pos是头节点,调用头删方法SLpopfront(plist);}else{SLNode* prev = *plist;while (prev->next != pos){//找到pos的前一个节点prev = prev->next;}//prev pos pos->nextprev->next = pos->next;free(pos);pos = NULL;}
}

测试该代码: 老铁没毛病!

3.12删除pos节点之后的节点

删除pos节点之后的节点我们要改变指向的指针就只有一个了,那就是pos->next指针。要让它指向pos->next->next。我们画图分析:

//删除pos节点后一个节点
void slpopposback(SLNode* pos)
{assert(pos && pos->next);//pos->next != NULL避免pos是尾节点SLNode* Next = pos->next;pos->next = pos->next->next;free(Next);Next = NULL;
}

 测试代码:

那如果我们的pos节点就是尾节点程序会发生什么呢? 

我们看到,上面提醒我们断言出错,说明此时pos节点为尾节点。 

3.13销毁链表

//销毁链表
void DestorySL(SLNode** plist)
{assert(plist && *plist);SLNode* Next = (*plist)->next;while (Next){free(*plist);*plist = Next;Next = Next->next;}free(*plist);*plist = NULL;
}

 我们通过调试来判断我们的销毁方法:没有问题!

到这里,我们单链表的所有方法都已经完成了。下面附上完整代码:

四.完整代码

1.singlelist.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>//节点数据类型
typedef int SingleListdatatype;//链表节点
typedef struct SingleListNode
{SingleListdatatype val;struct SingleListNode* next;
}SLNode;//节点的创建
SLNode* BuyNode(SingleListdatatype x);//链表的打印
void SLNodePrint(SLNode* plist);//尾插
void SLNodepushback(SLNode** plist, SingleListdatatype x);//头插
void SLpushfront(SLNode** plist, SingleListdatatype x);//尾删
void SLpopback(SLNode** plist);//头删
void SLpopfront(SLNode** plist);//查找
SLNode* SLFind(SLNode* plist, SingleListdatatype x);//在指定位置之前插入数据
void SLposfront(SLNode** plist, SLNode* pos,SingleListdatatype x);//在指定位置之后插入数据
void SLposback(SLNode* pos, SingleListdatatype x);//删除pos节点
void slpoppos(SLNode** plist, SLNode* pos);//删除pos节点后一个节点
void slpopposback(SLNode* pos);//销毁链表
void DestorySL(SLNode** plist);

2.singlelist.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "SingleList.h"//节点的创建
SLNode* BuyNode(SingleListdatatype x)
{SLNode* node = (SLNode*)malloc(sizeof(SLNode));if (node == NULL){perror("malloc");exit(-1);}node->val = x;node->next = NULL;return node;
}//链表的打印
void SLNodePrint(SLNode* plist)
{while (plist)//plist != NULL{printf("%d->", plist->val);plist = plist->next;}printf("NULL\n");
}//尾插
void SLNodepushback(SLNode** plist, SingleListdatatype x)
{assert(plist);SLNode* newnode = BuyNode(x);//新节点if (*plist == NULL){//空链表*plist = newnode;}else{//非空链表//遍历原链表,找到尾节点SLNode* pcur = *plist;while (pcur->next)//(*plist)->next != NULL{pcur = pcur->next;}pcur->next = newnode;}
}//头插
void SLpushfront(SLNode** plist, SingleListdatatype x)
{assert(plist);SLNode* newnode = BuyNode(x);newnode->next = *plist;*plist = newnode;
}//尾删
void SLpopback(SLNode** plist)
{assert(plist && *plist);//*plist != NULL防止链表为空if (!((*plist)->next))//(*plist)->next == NULL{//链表只有一个节点free(*plist);*plist = NULL;}else{//链表有多个节点SLNode* pcur = *plist;//记录倒数第二个节点SLNode* del = *plist;//记录尾节点while (del->next){pcur = del;del = del->next;}pcur->next = NULL;free(del);del = NULL;}
}//头删
void SLpopfront(SLNode** plist)
{assert(plist && *plist);//链表不能为空SLNode* next = (*plist)->next;free(*plist);*plist = next;
}//查找
SLNode* SLFind(SLNode* plist, SingleListdatatype x)
{assert(plist);SLNode* pcur = plist;while (pcur){if (pcur->val == x){return pcur;}pcur = pcur->next;}return NULL;
}//在指定位置之前插入数据
void SLposfront(SLNode** plist, SLNode* pos, SingleListdatatype x)
{assert(plist && *plist);//链表不能为空,否则找不到指定位置assert(pos);if (pos == *plist){//pos节点就是第一个节点//调用头插方法SLpushfront(plist,x);}else{//pos节点不是第一个节点SLNode* newnode = BuyNode(x);SLNode* pcur = *plist;SLNode* prev = *plist;while (pcur){if (pcur->val == pos->val){break;}prev = pcur;pcur = pcur->next;}// prev newnode pcur/posprev->next = newnode;newnode->next = pcur;}
}//在指定位置之后插入数据
void SLposback(SLNode* pos, SingleListdatatype x)
{assert(pos);SLNode* newnode = BuyNode(x);//pos newnode pos->nextnewnode->next = pos->next;pos->next = newnode;
}//删除pos节点
void slpoppos(SLNode** plist, SLNode* pos)
{assert(plist && *plist);assert(pos);if (*plist == pos){//pos是头节点,调用头删方法SLpopfront(plist);}else{SLNode* prev = *plist;while (prev->next != pos){//找到pos的前一个节点prev = prev->next;}//prev pos pos->nextprev->next = pos->next;free(pos);pos = NULL;}
}//删除pos节点后一个节点
void slpopposback(SLNode* pos)
{assert(pos && pos->next);//pos->next != NULL避免pos是尾节点SLNode* Next = pos->next;pos->next = pos->next->next;free(Next);Next = NULL;
}//销毁链表
void DestorySL(SLNode** plist)
{assert(plist && *plist);SLNode* Next = (*plist)->next;while (Next){free(*plist);*plist = Next;Next = Next->next;}free(*plist);*plist = NULL;
}

3.test.c 

#define _CRT_SECURE_NO_WARNINGS 1
#include "SingleList.h"//void test1()
//{
//	SLNode* plist1 = BuyNode(1);
//	SLNode* plist2 = BuyNode(2);
//	SLNode* plist3 = BuyNode(3);
//	SLNode* plist4 = BuyNode(4);
//
//	plist1->next = plist2;
//	plist2->next = plist3;
//	plist3->next = plist4;
//
//	SLNodePrint(plist1);
//}void test2()
{SLNode* plist = NULL;//测试尾插SLNodepushback(&plist, 1);SLNodepushback(&plist, 2);SLNodepushback(&plist, 3);SLNodepushback(&plist, 4);SLNodePrint(plist);//测试销毁链表DestorySL(&plist);//测试删除pos节点的下一个节点//SLNode* find = SLFind(plist, 4);//slpopposback(find);//SLNodePrint(plist);//测试删除pos节点//SLNode* find = SLFind(plist, 4);//slpoppos(&plist,find);//SLNodePrint(plist);//测试在指定位置之后插入数据//SLNode* find = SLFind(plist, 1);//SLposback(find,66);//SLNodePrint(plist);//测试在指定位置之前插入数据//SLNode* find = SLFind(plist, 4);//SLposfront(&plist,find,100);//SLNodePrint(plist);//测试查找//SLNode* find = SLFind(plist,99);//if (find == NULL)//{//	printf("找不到!");//}//else//{//	printf("找到了!");//}//测试头删//SLpopfront(&plist);//SLNodePrint(plist);//2 3 4//SLpopfront(&plist);//SLNodePrint(plist);//3 4//SLpopfront(&plist);//SLNodePrint(plist);//4//SLpopfront(&plist);//SLNodePrint(plist);//NULL//SLpopfront(&plist);//SLNodePrint(plist);//测试尾删//SLpopback(&plist);//SLNodePrint(plist);//1 2 3//SLpopback(&plist);//SLNodePrint(plist);//1 2//SLpopback(&plist);//SLNodePrint(plist);//1//SLpopback(&plist);//SLNodePrint(plist);//NULL//测试头插//SLpushfront(&plist,100);//SLNodePrint(plist);//SLpushfront(&plist,99);//SLNodePrint(plist);
}int main()
{//test1();test2();return 0;
}

完!

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

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

相关文章

【数据结构】第五讲:栈和队列

个人主页&#xff1a;深情秋刀鱼-CSDN博客 数据结构专栏&#xff1a;数据结构与算法 源码获取&#xff1a;数据结构: 上传我写的关于数据结构的代码 (gitee.com) 目录 一、栈 1.栈的定义 2.栈的实现 a.栈结构的定义 b.初始化 c.扩容 d.入栈 e.出栈 f.打印 g.取栈顶元素…

【CTF Crypto】XCTF GFSJ0522 base64 Writeup(Base64编码)

base64 元宵节灯谜是一种古老的传统民间观灯猜谜的习俗。 因为谜语能启迪智慧又饶有兴趣&#xff0c;灯谜增添节日气氛&#xff0c;是一项很有趣的活动。 你也很喜欢这个游戏&#xff0c;这不&#xff0c;今年元宵节&#xff0c;心里有个黑客梦的你&#xff0c;约上你青梅竹马…

Git泄露(续)

接上一篇补充 git config --global user.name " " git config --global user.email 邮箱地址 配置用户名和邮箱 git commit 使其处于交互区&#xff0c;没有使用 -m&#xff0c;默认用vim 来编辑和提交信息 输入要提交的内容&#xff0c;然后按ESC建回到命令…

「 网络安全常用术语解读 」通用配置枚举CCE详解

1. 背景介绍 NIST提供了安全内容自动化协议&#xff08;Security Content Automation Protocol&#xff0c;SCAP&#xff09;为漏洞描述和评估提供一种通用语言。SCAP组件包括&#xff1a; 通用漏洞披露(Common Vulnerabilities and Exposures, CVE)&#xff1a;提供一个描述…

泰迪智能科技企业数据挖掘流程分析及特色服务优势

企业发展会沉淀大量的数据&#xff0c;数据中囊括了企业业务各种维度指标&#xff0c;通过数据挖掘和数据分析 &#xff0c;让企业业务了解过去、现在和未来将要发生什么&#xff0c;从而更好的调整企业发展方向。泰迪智能科技企业数据挖掘平台是面向企业级用户快速处理数据构建…

C++学习第二十八课:C++ 中的智能指针详解

在 C 中&#xff0c;内存管理是每个程序员都需要面对的问题。在处理动态分配的内存时&#xff0c;如果忘记释放内存&#xff0c;可能会导致内存泄漏。为了解决这个问题&#xff0c;C11 引入了智能指针的概念。本文将详细介绍 C 中使用智能指针的方法&#xff0c;并结合实际案例…

永倍达 最新消息!发放消费券! 重新开网?

大家好 我是一家软件开发公司的产品经理 吴军 我又又又又又叕来蹭热度了&#xff0c;最近永倍达有新动作&#xff01;发放消费券&#xff1f; 店长群最新通知 4.15号开始发放消费券 一个月之后才可以重新提现 今天是5.10号离5.18也不远了 大家拭目以待看看到底能不能提现&a…

“紧急联系人功能”的智慧守护

在当今科技融入生活的每一个角落&#xff0c;一款名为“蝙蝠避障”专为盲人设计的辅助应用正悄然改变着视障群体的日常出行体验。这款应用中&#xff0c;一个看似不起眼却至关重要的功能——“紧急联系人功能”&#xff0c;以其强大的实用性和人文关怀&#xff0c;成为了盲人朋…

一套3D PACS系统源码:可实现医学影像获取、存档、观片、处理、打印多项应用、基于C#+VC + MSSQL开发的全套PACS源码

一套3D PACS系统源码&#xff1a;可实现医学影像获取、存档、观片、处理、打印多项应用 PACS的功能价值在于通过连接不同的影像设备&#xff0c;存储与管理图像&#xff0c;图像的调用与后处理&#xff0c;实现资源共享&#xff0c;降低成本&#xff0c;达到提高工作效率、提升…

halcon学习之一维测量基础

目录 创建测量矩形&#xff0c;获取测量句柄 gen_measure_rectangle2&#xff08;&#xff09; 使用句柄进行测量 measure_pos&#xff08;&#xff09; 修改参数Threshold 修改参数Transition 修改参数select 参数RowEdge&#xff0c;ColumnEdge&#xff0c;Distance …

4步快速配置Java、MySQL、Maven环境(windows)

每次入职一家新公司或者用一台其他的临时电脑或者新电脑时都要重新配置Java开发环境&#xff0c;很麻烦&#xff0c;因此我在这里记录一下快速配置环境的方式&#xff0c;四步搞定&#xff01;此处以win为操作系统进行讲解。 第一步&#xff1a;下载链接 下载链接&#xff1a…

【JavaWeb】网上蛋糕商城后台-商品管理

概念 本文讲解和实现网上蛋糕商城的后台管理系统中的商品管理功能。 商品列表 点击后台管理系统的head.jsp头部的“商品管理”功能选项&#xff0c;向服务器发送请求/admin/goods_list 因此需要在servlet包中创建AdminGoodsListServlet类&#xff0c;用于获取商品信息列表 …

zblog中用户中心-邀请码注册插件的导出功能补充

自己加了一个导出未使用的邀请码功能&#xff0c;可惜我不是入驻作者&#xff0c;没有权限发布&#xff0c;之前被一条大河拒了&#xff0c;他说我抄他代码&#xff0c;不给我过审还冷嘲热讽&#xff0c;我一气之下&#xff0c;就没继续申请了&#xff0c;话说我是专业搞java开…

使用JavaScript日历小部件和DHTMLX Gantt的应用场景(二)

DHTMLX Suite UI 组件库允许您更快地构建跨平台、跨浏览器 Web 和移动应用程序。它包括一组丰富的即用式 HTML5 组件&#xff0c;这些组件可以轻松组合到单个应用程序界面中。 DHTMLX Gantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表&#xff0c;可满足项目管理应用…

HTML【安装HBuilder、常用标签】--学习JavaEE的day44

day44 JavaEE 学习过程&#xff1a;前端—>数据库—>服务器端 前端的VUE在框架阶段学习 JavaEE学习过程图 HTML 前端&#xff1a;展示页面、与用户交互 — HTML 后端&#xff1a;数据的交互和传递 — JavaEE/JavaWeb 1. 前端开发的工作模式 开发输出htmlcssjs 理解&am…

PHP的XML格式和AJAX

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;PHP &#x1f4da;参考教程&#xff1a;菜鸟\编程网❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、PHP的XML格式 PHP XML Expat 解析器 XML 是什么…

lora体验

项目链接 GitHub - cloneofsimo/lora: Using Low-rank adaptation to quickly fine-tune diffusion models. 现在如果想体验stable diffusion的lora功能&#xff0c;有很多种渠道吧&#xff0c;虽然lora是微软开源&#xff0c;但是cloneofsimo提供了适配stable diffusion的lor…

Springboot+Vue项目-基于Java+MySQL的车辆管理系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

STM32(六):定时器PWM呼吸灯 (标准库函数)

前言 上一篇文章已经介绍了如何用STM32单片机中的TIMER定时器来控制LED灯的交替闪烁&#xff0c;实现了点灯的第五种方式。这篇文章我们来介绍一下如何用STM32单片机中的定时器的PWM波来实现LED的“呼吸”。 一、实验原理 关于定时器这边就不多加赘述&#xff0c;详细请看上…

综合能力 | 误差 | 学习笔记

误差指真值与观测值的差值。 误差分为系统误差&#xff08;消除方法&#xff1a;观测方法、仪器校正、修正等&#xff09;、偶然误差&#xff08;符合正态分布规律&#xff0c;进行计算纠正&#xff09;、粗差&#xff08;一般舍弃&#xff09;。 中误差&#xff08;均方差或标…