当前位置: 首页 > news >正文

数据结构入门篇 之 【双链表】的实现讲解(附完整实现代码及顺序表与线性表的优缺点对比)

在这里插入图片描述
一日读书一日功,一日不读十日空
书中自有颜如玉,书中自有黄金屋

一、双链表

1、双链表的结构

2、双链表的实现

1)、双向链表中节点的结构定义

2)、初始化函数 LTInit

3)、尾插函数 LTPushBack

4)、头插函数 LTPushFront

5)、尾删函数 LTPopBack

6)、头删函数 LTPopFront

7)、查找函数 LTFind

8)、在指定位置之后插入数据函数 LTInsert

9)、删除指定位置数据函数 LTErase

10)、销毁函数 LTDesTroy

二、双链表完整代码

三、顺序表和链表的优缺点对比

四、完结撒❀

前言

学习前先思考3个问题:

1.顺序表和链表的关系是什么?
2.链表的分类有哪些?
3.顺序表和链表的优缺点有哪些?

–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–
1.顺序表和链表的关系是什么

我们之前学习了“顺序表”,“单链表”。
链表和顺序表都是线性表
线性表是指

逻辑结构:一定是线性的。
物理结构:不一定是线性的。

在这里插入图片描述物理结构是指表在内存中开辟的空间结构,顺序表的物理结构是连续的,而链表的物理结构是不连续的,但它们的逻辑结构都是连续的。

2.链表的分类有哪些?
链表根据带头或者不带头单向或者双向循环或者不循环一共分为8种
我们之前所学的单链表全名是叫:不带头单向不循环链表,而现在要学习的双链表是叫带头双向循环链表
双链表:
在这里插入图片描述掌握单链表和双链表对于其他链表的实现也就不那么困难了。

3.顺序表和链表的优缺点有哪些?
这里涉及到顺序表和链表的对比,先讲解双向链表,这放到博客末尾为大家对比讲解

一、双链表

1、双链表的结构

在这里插入图片描述注意:这里的“带头”跟前面我们说的“头节点”是两个概念。
带有节点里的头节点实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在这里“放哨的”

“哨兵位”存在的意义:
遍历循环链表避免出现死循环。

2、双链表的实现

对于双向链表的实现,我们依然使用List.h,List.c,test.c,三个文件进行实现。

1)、双向链表中节点的结构定义

上面我们简单介绍过双链表,其全名为:带头双向循环链表

带头:指链表中带有哨兵位
双向:双链表的每个节点内含有两个链表指针变量,分别指向前一个节点和后一个节点,所以就可以通过一个节点找到这个节点前后的两个节点。
循环:链表中的每个节点互相连接,最后一个节点与哨兵位相连构成一个环,整体逻辑结构可以进行循环操作

代码如下:

//定义双向链表中节点的结构
typedef int LTDataType;
typedef struct ListNode
{struct ListNode* prev;LTDataType data;struct ListNode* next;
}LTNode;

这里将结构体进行了重命名为LTNode。

2)、初始化函数 LTInit

在创建双链表中的哨兵位时我们需要对其进行初始化,防止意料之外的情况发生。
根据所传形参的类型不同,我们有两种写法
代码如下:

方案1

//方案1
void LTInit(LTNode** pphead)
{(*pphead) = (LTNode*)malloc(sizeof(LTNode));if (*pphead == NULL){perror("mallic:");exit(1);}(*pphead)->data = -1;(*pphead)->prev = (*pphead)->next = *pphead;
}

方案2

LTNode* LTBuyNode(LTDataType x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){perror("malloc:");exit(1);}newnode->data = x;newnode->next = newnode->prev = newnode;return newnode;
}//方案2
LTNode* LTInit()
{LTNode* phead = LTBuyNode(-1);return phead;
}

方案2里面包含了节点空间申请的函数,只是简单的创建双链表的节点,这里就不展开讲解了。

3)、尾插函数 LTPushBack

老规矩,我们开始实现管理链表数据的函数,这里讲的是头插。
在这里插入图片描述假如我们要在链表中尾插一个6,那么我们是需要先创建一个节点来存储6,下面分两步:

1.将6的节点里面前(prev)后(next)链表指针变量对应与原链表的尾节点d3和哨兵位head进行连接
2.将原链表尾节点d3的后链表指针变量(next)指向6的节点,再将哨兵位head的前链表指针变量(prev)指向6的节点

完成上面两部就实现了节点的插入。
代码如下:

//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);newnode->prev = phead->prev;newnode->next = phead;phead->prev->next = newnode;phead->prev = newnode;
}

这里的LTBuyNode函数在初始化函数中提到过。

4)、头插函数 LTPushFront

实现了尾插,头插也是大同小异。
头插是指在哨兵位后面的进行插入,第一个有效节点之前插入,即为头插。
在这里插入图片描述根据上图,进行头插

1.改变插入节点6的前后链表指针变量的指向。
2.再分别改变哨兵位head后链表指针变量(next)和第一个有效节点d1的前链表指针变量(prev)的指向。

代码如下:

//头插
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);newnode->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;phead->next = newnode;
}

也是简简单单。

5)、尾删函数 LTPopBack

尾删函数的操作如下图:
在这里插入图片描述
1.改变哨兵位的前链表指针变量,指向原链表(还没尾删时的链表)尾节点d3的前链表指针变量(即倒数第二个节点d2的地址)。
2.相反,再将d2节点的后链表指针变量(next)指向哨兵位head。

代码如下:

//尾删
void LTPopBack(LTNode* phead)
{assert(phead);//链表只有一个哨兵位也不行assert(phead != phead->next);LTNode* del = phead->prev;//要进行尾删的节点LTNode* ddel = del->prev;//要进行删除的前一节点phead->prev = ddel;ddel->next = phead;free(del);del = NULL;
}

记得最后将尾删的节点空间进行释放。

6)、头删函数 LTPopFront

头删函数的操作如下图:
在这里插入图片描述与尾删也是大同小异
代码如下:

//头删 在哨兵位之后进行删除
void LTPopFront(LTNode* phead)
{assert(phead);assert(phead != phead->next);LTNode* del = phead->next;//要进行删除的节点LTNode* ddel = del->next;//要进行删除节点的下一个节点phead->next = ddel;ddel->prev = phead;free(del);del = NULL;
}

7)、查找函数 LTFind

既然要查找,那么肯定需要遍历链表并且也要保证链表不为空

在双链表中,当链表中只剩下哨兵位,那么这个链表即为空链表。

代码如下:

//查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);assert(phead != phead->next);//遍历链表LTNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

断言时进行的assert(phead != phead->next);便是判断链表是否为空的条件。
而while (pcur != phead)是判断是否将链表遍历完的条件。
找到的话就返回给节点的地址,没有找到就返回空指针。

8)、在指定位置之后插入数据函数 LTInsert

操作过程如下图所示:
在这里插入图片描述假设我们在节点d2后面进行节点的插入,那么会受到影响的就是d2节点的后链表指针变量(next)和d3节点的前链表指针变量(prev),需要执行的操作:

1.将newnode节点的前链表指针变量(prev)指向d2节点,再将newnode节点的后链表指针变量(next)指向d3节点
2.将d3节点的前链表指针变量(prev)指向newnode’节点,再将d2节点的后链表指针变量(next)指向newnode节点。

注意! 第2步指针变量改变指向的先后顺序不能改变,不然指向地址不正确!

代码如下;

//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = LTBuyNode(x);newnode->prev = pos;newnode->next = pos->next;pos->next->prev = newnode;pos->next = newnode;
}

9)、删除指定位置数据函数 LTErase

操作过程如下图所示:
在这里插入图片描述假设删除d3节点,很明显这就是尾删操作,所以删除指定位置数据与其他删除函数也是一样的原理,其影响到的就是删除节点前后的节点链表指针的指向。

代码如下:

//删除pos位置的数据
void LTErase(LTNode* pos)
{assert(pos);LTNode* del = pos->next;//pos之后的数据LTNode* front = pos->prev;//pos之前的数据front->next = del;del->prev = front;free(pos);pos = NULL;
}

重要的是最在要记得将删除的节点空间进行销毁。

10)、销毁函数 LTDesTroy

那么最后的一个函数,销毁函数。
创建双建表使用后我们一定不要忘记进行销毁,将开辟的内存空间归还给计算机,不然在以后中可能会出现内存泄漏的工作事故。
销毁函数也根据传参类型不同有两种方案
代码如下:
方案1

//方案1
void LTDesTroy(LTNode* phead)
{assert(phead);assert(phead != phead->next);LTNode* pcur = phead->next;while (pcur != phead){LTNode* next = pcur->next;free(pcur);pcur = next;}
}

方案2

//方案2
void LTDesTroy(LTNode** pphead)
{assert(pphead);assert(*pphead);LTNode* pcur = (*pphead)->next;while (pcur != (*pphead)){LTNode* next = pcur->next;free(pcur);pcur = next;}free((*pphead));(*pphead) = NULL;
}

对比之下方案1所传的形参为一级指针,而方案2为二级指针,因此我们是可以在方案2中直接对形参解引用得到双链表的哨兵位进行释放,而方案1并不行。
所以大家评判一下是那种方案更好呢?
其实是方案1更好,因为我们需要保持接口一致性,细心的同学可能已经发现了,之前所写的函数形参都为一级指针,所以我们在写代码的时候保持接口一致性也是很重要的,所以方案1更合适一些,至于链表中哨兵位的释放,我们下面在销毁函数外(主函数内)进行销毁即可。

二、双链表完整代码

List.h:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>//定义双向链表中节点的结构
typedef int LTDataType;
typedef struct ListNode
{struct ListNode* prev;LTDataType data;struct ListNode* next;
}LTNode;//注意双向链表是带有哨兵位的,插入数据之前链表中必须先插入一个哨兵位//void LTInit(LTNode** pphead);
LTNode* LTInit();
void LTDesTroy();//尾插
void LTPushBack(LTNode* phead, LTDataType x);//头插
void LTPushFront(LTNode* phead, LTDataType x);//尾删
void LTPopBack(LTNode* phead);
//头删
void LTPopFront(LTNode* phead);//查找
LTNode* LTFind(LTNode* phead,LTDataType x);//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x);
//删除pos位置的数据
void LTErase(LTNode* pos);

List.c:

#include "List.h" LTNode* LTBuyNode(LTDataType x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){perror("malloc:");exit(1);}newnode->data = x;newnode->next = newnode->prev = newnode;return newnode;
}//方案1
//void LTInit(LTNode** pphead)
//{
//	(*pphead) = (LTNode*)malloc(sizeof(LTNode));
//	if (*pphead == NULL)
//	{
//		perror("mallic:");
//		exit(1);
//	}
//
//	(*pphead)->data = -1;
//	(*pphead)->prev = (*pphead)->next = *pphead;
//}//方案2
LTNode* LTInit()
{LTNode* phead = LTBuyNode(-1);return phead;
}//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);newnode->prev = phead->prev;newnode->next = phead;phead->prev->next = newnode;phead->prev = newnode;
}void LTPrint(LTNode* phead)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}//头插
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);newnode->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;phead->next = newnode;
}//尾删
void LTPopBack(LTNode* phead)
{assert(phead);//链表只有一个哨兵位也不行assert(phead != phead->next);LTNode* del = phead->prev;//要进行尾删的节点LTNode* ddel = del->prev;//要进行删除的前一节点phead->prev = ddel;ddel->next = phead;free(del);del = NULL;
}//头删 在哨兵位之后进行删除
void LTPopFront(LTNode* phead)
{assert(phead);assert(phead != phead->next);LTNode* del = phead->next;//要进行删除的节点LTNode* ddel = del->next;//要进行删除节点的下一个节点phead->next = ddel;ddel->prev = phead;free(del);del = NULL;
}//查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);assert(phead != phead->next);//遍历链表LTNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}//删除pos位置的数据
void LTErase(LTNode* pos)
{assert(pos);LTNode* del = pos->next;//pos之后的数据LTNode* front = pos->prev;//pos之前的数据front->next = del;del->prev = front;free(pos);pos = NULL;
}//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = LTBuyNode(x);newnode->prev = pos;newnode->next = pos->next;pos->next->prev = newnode;pos->next = newnode;
}//方案1
void LTDesTroy(LTNode* phead)
{assert(phead);assert(phead != phead->next);LTNode* pcur = phead->next;while (pcur != phead){LTNode* next = pcur->next;free(pcur);pcur = next;}
}//方案2
//void LTDesTroy(LTNode** pphead)
//{
//	assert(pphead);
//	assert(*pphead);
//
//	LTNode* pcur = (*pphead)->next;
//	while (pcur != (*pphead))
//	{
//		LTNode* next = pcur->next;
//		free(pcur);
//		pcur = next;
//	}
//	free((*pphead));
//	(*pphead) = NULL;
//}

三、顺序表和链表的优缺点对比

学到这里大家会感觉双链表听起来可能比较复杂,但学完之后感觉比顺序表和单链表还容易,事实就是如此。
顺序表和双向链表优缺点分析:
在这里插入图片描述由上图,并不是双链表一定比顺序表好。
顺序表和双链表各有优势,我们在使用中要根据实际情况选择适合的线性表进行存储就是最好的。

四、完结撒❀

如果以上内容对你有帮助不妨点赞支持一下,以后还会分享更多计算机知识,我们一起进步。
最后我想讲的是,据说点赞的都能找到漂亮女朋友
在这里插入图片描述

相关文章:

数据结构入门篇 之 【双链表】的实现讲解(附完整实现代码及顺序表与线性表的优缺点对比)

一日读书一日功&#xff0c;一日不读十日空 书中自有颜如玉&#xff0c;书中自有黄金屋 一、双链表 1、双链表的结构 2、双链表的实现 1&#xff09;、双向链表中节点的结构定义 2&#xff09;、初始化函数 LTInit 3&#xff09;、尾插函数 LTPushBack 4&#xff09;、头…...

什么是零日攻击?

一、零日攻击的概念 零日攻击是指利用零日漏洞对系统或软件应用发动的网络攻击。 零日漏洞也称零时差漏洞&#xff0c;通常是指还没有补丁的安全漏洞。由于零日漏洞的严重级别通常较高&#xff0c;所以零日攻击往往也具有很大的破坏性。 目前&#xff0c;任何安全产品或解决方案…...

阿里云2025届春招实习生招聘

投递时间&#xff1a;2024年2月1日-2026年3月1日 岗位职责 负责大型客户“上云”&#xff0c;"用云"技术平台开发。 开发云迁移运维技术工具&#xff0c;帮助阿里云服务团队&&企业客户和服务商自主、高效的完成云迁移。 开发云运维技术工具&#xff0c;帮助…...

简单了解多线程

并发和并行 并发&#xff1a; 在同一时刻&#xff0c;多个指令在单一CPU上交替指向 并行&#xff1a;在同一时刻&#xff0c;多个指令在多个CPU上同时执行 2核4线程&#xff0c;4核8线程&#xff0c;8核16线程&#xff0c;16核32线程 基础实现线程的方式 Thread :继承类 &…...

GEE对上传并读取CSV文件

首先在Assets中上传csv csv格式如下所示&#xff1a; 上传好了之后&#xff0c;来看看这个表能否显示 var table ee.FeatureCollection("projects/a-flyllf0313/assets/dachang_2022"); var sortedTable table.sort(id); // 替换 propertyName 为你想要排序的属性…...

vulnhub-----SickOS靶机

文章目录 1.信息收集2.curl命令反弹shell提权利用POC 1.信息收集 ┌──(root㉿kali)-[~/kali/vulnhub/sockos] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:10:3c:9b, IPv4: 10.10.10.10 Starting arp-scan 1.9.8 with 256…...

slab分配器

什么是slab分配器&#xff1f; 用户态程序可以使用malloc及其在C标准库中的相关函数申请内存&#xff1b;内核也需要经常分配内存&#xff0c;但无法使用标准库函数&#xff1b;linux内核中&#xff0c;伙伴分配器是一种页分配器&#xff0c;是以页为单位的&#xff0c;但这个…...

MySQL面试题之基础夯实

一、mysql当中的基本数据类型有哪些 MySQL中的基本数据类型包括但不限于以下几大类&#xff1a; 数值类型&#xff1a; 整数类型&#xff1a;TINYINT、SMALLINT、MEDIUMINT、INT&#xff08;INTEGER&#xff09;、BIGINT浮点数类型&#xff1a;FLOAT、DOUBLE、DECIMAL&#xf…...

feign请求添加拦截器

FeignClient 的 configuration 属性&#xff1a; Feign 注解 FeignClient 的 configuration 属性&#xff0c;可以对 feign 的请求进行配置。 包括配置Feign的Encoder、Decoder、 Interceptor 等。 feign 请求添加拦截器&#xff0c;也可以通过这个 configuration 属性 来指…...

蓝桥杯之简单数论冲刺

文章目录 取模快速幂 取模 这道题目有两个注意点&#xff1a; 1.当你的取模之后刚好等于0的话&#xff0c;后面就不用进行后面的计算 2.if sum detail[i] > q: 这个语句的等号也很重要 import os import sys# 请在此输入您的代码a,b,n map(int,input().split())week a*5 …...

Http的缓存有哪些

HTTP 缓存可以通过多种 HTTP 头部字段来控制&#xff0c;主要包括以下几种&#xff1a; 1.Expires&#xff1a;这个字段定义了响应的过期时间。如果当前时间小于 Expires 的时间&#xff0c;那么就可以直接使用缓存。 2.Cache-Control&#xff1a;这个字段是一个指令&#xff…...

Linux 网络虚拟化 Macvlan(基于物理网络接口虚拟网络接口) 认知

写在前面 博文内容涉及 Macvlan 的简单认知&#xff0c;以及一个Demo博文内容根据《 Kubernetes 网络权威指南&#xff1a;基础、原理与实践》 整理理解不足小伙伴帮忙指正 不必太纠结于当下&#xff0c;也不必太忧虑未来&#xff0c;当你经历过一些事情的时候&#xff0c;眼前…...

Spark-Scala语言实战(1)

在之前的文章中&#xff0c;我们学习了如何在Linux安装Spark以及Scala&#xff0c;想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 Spark及Scala的安装https:/…...

NBlog Java定时任务-备份MySQL数据

NBlog部署维护流程记录&#xff08;持续更新&#xff09;&#xff1a;https://blog.csdn.net/qq_43349112/article/details/136129806 为了避免服务器被攻击&#xff0c;给博客添加了一个MySQL数据备份功能。 此功能是配合博客写的&#xff0c;有些方法直接用的已有的&#xf…...

微信小程序项目实战遇到的问题

我们以学生成绩平台来作为例子。这是我们想得到的效果。 以下是完整代码&#xff1a; index.js // index.js Page({//页面的初始数据data: {hello: 欢迎进入微信小程序的编程世界,score: 80,userArray: [{name: 张三,score: [66, 77, 86, 70, 90]},{name: 李四,score: [88, 7…...

网络原理(3)——TCP协议

目录 一、连接管理 二、三次握手 1、何为三次握手&#xff1f; 2、三次握手有何意义&#xff1f; 三、四次挥手 三次握手和四次挥手的相似之处和不同之处 &#xff08;1&#xff09;相似之处 &#xff08;2&#xff09;不同之处 四、TCP的状态 建立连接&#xff1a; 断开…...

nginx多级代理配置获取客户端真实ip

流量路径 #mermaid-svg-NX785p8k6RVBngHY {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-NX785p8k6RVBngHY .error-icon{fill:#552222;}#mermaid-svg-NX785p8k6RVBngHY .error-text{fill:#552222;stroke:#552222;}#…...

Django框架的全面指南:从入门到高级【第128篇—Django框架】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Django框架的全面指南&#xff1a;从入门到高级 Django是一个高效、功能强大的Python Web框…...

C++类和对象基础

目录 类的认识 访问限定符&#xff1a;public(公有)&#xff0c;protected(保护)&#xff0c;private(私有)。 类的两种定义方式: 类的实例化&#xff1a; 封装&#xff1a; 类的对象大小的计算&#xff1a; 类成员函数的this指针&#xff1a; C语言是面向过程的语言&am…...

消息队列常见的两种消费模式

一、点对点模式 点对点模式&#xff1a;生产者发送消息到消息队列&#xff0c;消费者从消息队列中接收、处理消息&#xff0c;消息被消费后&#xff0c;就不在消息队列中了。每个消息只能由一个消费者接收和处理。如果有多个消费者监听同一个队列&#xff0c;消息将被发送到其…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing

Muffin 论文 现有方法 CRADLE 和 LEMON&#xff0c;依赖模型推理阶段输出进行差分测试&#xff0c;但在训练阶段是不可行的&#xff0c;因为训练阶段直到最后才有固定输出&#xff0c;中间过程是不断变化的。API 库覆盖低&#xff0c;因为各个 API 都是在各种具体场景下使用。…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...