数据结构之链表(1),单链表
目录
前言
一、什么是链表
二、链表的分类
三、单链表
四、单链表的实现
五、SList.c文件完整代码
六、使用演示
总结

前言
本文讲述了什么是链表,以及实现了完整的单链表。
❤️感谢支持,点赞关注不迷路❤️
一、什么是链表
1.概念
概念:链表是⼀种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
简单来说,链表属于线性表的一种,在逻辑结构上是连续的,物理结构上不一定是连续的,通过指针连接。
2.节点
与顺序表不同的是,链表里的每个节点都是独立申请下来的空间,我们称之为“结点/结点”
结点的组成主要有两个部分:当前结点要保存的数据和保存下⼀个结点的地址(指针变量)。也可以说成数据域和指针域两部分
特点:链表中每个结点都是独立申请的(即需要插入数据时才去申请一块结点的空间),我们需要通过指针变量来保存下一个结点位置,才能从当前结点找到下⼀个结点。
3.链表的性质
- 链式结构在逻辑上是连续的,在物理结构上不⼀定连续
- 结点一般是从堆上申请的
- 从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续
4.与顺序表的对比
- 顺序表的 中间/头部 插入删除数据的时间复杂度为O(n),而链表相同功能时间复杂度为O(1)
- 顺序表增容需要申请请新空间,拷贝数据,释放旧空间。会有不小的消耗。链表每次申请一个节点,不存在拷贝,释放旧空间。
- 顺序表增容一般是呈2倍的增长,势必会有⼀定的空间浪费。例如当前容量为100,满了以后增容到200, 我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。链表不存在空间浪费
以上是链表相对与顺序表的优点,但不代表顺序表一定不如链表,顺序表在有些场景下效率还是非常高,因此选择使用什么数据结构是看场景的要求
二、链表的分类
链表的结构非常多样,有带头不带头,单向或者双向,循环不循环,这三种属性情况组合起来就有8种(2x2x2)链表结构:

虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:单链表和双向带头循环链表:
- 无头单向非循环链表(单链表):结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
- 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现该结构会带来很多优势,后面我们代码实现了就知道了。
本文主要讲述单链表
三、单链表
单链表的结构如下图:

单链表的结构声明:
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;//节点数据
struct SListNode* next;//指向下一节点的指针变量
}SLTNode;
以下单链表同样由3个文件组成:
- SList.h:单链表的结构声明,各种函数声明
- SList.c:用于函数的具体实现
- test.c:用于测试单链表(自行测试)
四、单链表的实现
1.SList.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);
2.SList.c文件
1.SLTPrint函数
//打印
void SLTPrint(SLTNode* phead)
{SLTNode* pcur = phead;//循环打印while (pcur){printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL\n");
}
解析:该函数用于打印链表数据,使用一个pcur指针,只要不为空指针,就循环遍历下一个节点
2.SLTBuyNode函数
//申请节点
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函数申请,申请成功后将其数据域赋值为参数值,指针域赋值为空指针NULL
- 因为该函数主要为其他功能函数服务,因此只需写在 SList.c 文件中即可,无需在头文件SList.h 中声明
3.SLTPushBack函数
//尾插
//因为要修改plist本身,因此需要二级指针来接收
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{//断言pphead不能为空指针assert(pphead);//申请新节点SLTNode* newnode = SLTBuyNode(x);//如果pphead指向的第一个节点就是空指针if (*pphead == NULL){*pphead = newnode;}else{//找尾结点SLTNode* pcur = *pphead;while (pcur->next){pcur = pcur->next;}pcur->next = newnode;}
}
解析:
- 该函数功能为:在链表尾部插入一个数据。因此需要链表的头部指针和需要插入的数据x
- 为什么头节点参数是二级指针?答:因为需要修改原指针变量本身,我们知道函数传参分为传值传参和传址传参(&),想要修改原变量内容就需要传址传参,而这里原变量就是一个指向链表头节点的指针变量,因此原变量通过&传参,就需要二级指针来接收一级指针变量的地址。所以这里就是用二级指针,下面其他的函数同理。
- 申请完新节点,插入到链表尾部时要分2种情况,1链表为空,2链表不为空。
- 链表为空则直接将新节点插入链表即可,也就是将指向头结点的指针指向新节点。链表不为空就需要寻找尾结点,尾节点的特点就是next指针指向空。循环之后将尾节点的next指针修改为新节点即可。

4.SLTPushFront函数
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);//直接申请节点,然后修改新节点指向即可SLTNode* newnode = SLTBuyNode(x);newnode->next = *pphead;*pphead = newnode;
}
解析:
- 功能:在链表头部插入一个新节点。参数同样是一个二级指针和新节点的数据
- 头插比较简单,只需将新申请的节点的 next 指针指向链表头结点,然后让指向链表头结点的指针指向新节点即可。

5.SLTPopBack函数
//尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead && *pphead);//只有一个节点的情况if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{//两个及以上节点的情况//申请两个节点用于指向尾节点和倒数第二个节点SLTNode* ptail = *pphead;SLTNode* prev = NULL;while (ptail->next){prev = ptail;ptail = ptail->next;}//销毁尾结点,然后让倒数第二个节点next指向空free(ptail);ptail = NULL;prev->next = NULL;}
}
解析:
- 功能:删除链表的最后一个节点,参数为一个二级指针
- 首先断言,不能传空指针并且链表不能为空
- 尾删也要分两种情况,因为如果只要一个节点,那么就不需要找倒数第二个节点。所以分为链表只有一个节点的情况和有多个节点的情况
- 只有一个节点,那么我们直接 free 掉该节点,然后让指向头结点的指针置空。多个节点,那么我们就需要找到最后一个节点 ptail 以及倒数第二个节点 prev,使用while循环即可,只要ptail 的下一个节点为空就跳出循环,此时 prev 为倒数第二个节点,然后释放掉最后一个节点,修改倒数第二个节点的next指针即可。

6.SLTPopFront函数
//头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}
解析:
- 功能:删除链表的头部节点
- 因为参数 *pphead 就是指向链表的头结点,因此删除简单,先创建一个next指针保存头结点的下一个节点,然后释放掉头节点,更改 *pphead 的指向即可
7.SLTFind函数
//查找
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 的节点,返回该节点的地址。
- 因为查找操作不会影响头指针 phead,因此参数为一级指针即可
- 查找很简单,循环遍历链表,让 pcur 一直往后走,直到找到存储 x 的节点,返回该节点,没找到就返回空指针
8.SLTInsert函数
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);//如果指定位置是头结点if (*pphead == pos){//头插SLTPushFront(pphead, x);}else{//新节点SLTNode* newnode = SLTBuyNode(x);//prev用于找到pos前一个节点SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;}
}
解析:
- 功能:在指定位置之前插入数据,参数为头指针地址、指定位置(用SLTFind函数确定)、需插入的数据x。
- 首先断言,头指针和pos指针都不能为空,也就是链表不能为空
- 然后,也分两种情况,因为如果指定位置刚好是头结点,那么只需要使用头插函数即可。如果指定位置不是头结点,我们就需要找到 pos 指向的结点的前一个节点。定义一个指针变量 prev,让它从头往后找,只要它的 next 指针不是 pos,那么就继续往下个节点走,直到找到pos的前一个节点。
- 找到之后,让 prev 的 next 指针指向新节点,再让新节点的 next 指针指向pos就完成了在指定位置前插入数据。

7.SLTInsertAfter函数
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTBuyNode(x);//先让newnode的next指向下一个节点newnode->next = pos->next;//再修改pos的next指针pos->next = newnode;
}
解析:
- 功能:在指定位置之后插入数据。
- 因为受影响的节点只有pos指向的节点,所以只需要传pos指针和需要增加的数据 x 即可。
- pos指针使用SLTFind函数指定即可
- 实现简单,注意顺序即可,将新节点的next指针指向pos节点的下一个节点,然后让pos指针的next指针指向新节点即可。

8.SLTErase函数
//删除指定位置pos的数据
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && *pphead);assert(pos);//如果pos位置是第一个位置if (*pphead == pos){//头删SLTPopFront(pphead);}else{//找到pos前一个节点SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//释放prev->next = pos->next;free(pos);pos = NULL;}
}
解析:
- 功能:删除指定位置的节点。
- 参数需用到头节点和pos指向节点
- 也要分两种情况,因为如果需删除节点刚好是头结点,直接头删即可,如果不是头结点,则需要找到指定位置的前一个节点,老套路,找到之后,先修改 prev 的next指针,然后释放pos指向的节点。

9.SLTEraseAfter函数
//删除pos之后的数据
void SLTEraseAfter(SLTNode* pos)
{assert(pos && pos->next);SLTNode* del = pos->next;//让pos指向它之后的之后的数据pos->next = pos->next->next;free(del);del = NULL;
}
解析:
- 功能:删除pos位置后一个节点
- 只需要pos指针即可
- 实现简单,先保存需删除的节点地址,然后改变pos的next指针指向,最后释放掉 del 即可。
10.SListDestroy函数
//销毁链表
void SListDestroy(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* pcur = *pphead;//循环销毁链表while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}//记得将头指针指置空*pphead = NULL;
}
解析:
- 功能:销毁整个链表
- 断言确保链表不为空
- 创建pcur循环遍历链表,只要pcur不为空,释放该空间。
- 最后记得将头指针置空
五、SList.c文件完整代码
#include "SList.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;
}//尾插
//因为要修改plist本身,因此需要二级指针来接收
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{//断言pphead不能为空指针assert(pphead);//申请新节点SLTNode* newnode = SLTBuyNode(x);//如果pphead指向的第一个节点就是空指针if (*pphead == NULL){*pphead = newnode;}else{//找尾结点SLTNode* pcur = *pphead;while (pcur->next){pcur = pcur->next;}pcur->next = newnode;}
}//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);//直接申请节点,然后修改新节点指向即可SLTNode* newnode = SLTBuyNode(x);newnode->next = *pphead;*pphead = newnode;
}//尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead && *pphead);//只有一个节点的情况if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{//两个及以上节点的情况//申请两个节点用于指向尾节点和倒数第二个节点SLTNode* ptail = *pphead;SLTNode* prev = NULL;while (ptail->next){prev = ptail;ptail = ptail->next;}//销毁尾结点,然后让倒数第二个节点next指向空free(ptail);ptail = NULL;prev->next = NULL;}
}//头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* 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 (*pphead == pos){//头插SLTPushFront(pphead, x);}else{//新节点SLTNode* newnode = SLTBuyNode(x);//prev用于找到pos前一个节点SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;}
}//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTBuyNode(x);//先让newnode的next指向下一个节点newnode->next = pos->next;//再修改pos的next指针pos->next = newnode;
}//删除指定位置pos的数据
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && *pphead);assert(pos);//如果pos位置是第一个位置if (*pphead == pos){//头删SLTPopFront(pphead);}else{//找到pos前一个节点SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//释放prev->next = pos->next;free(pos);pos = NULL;}
}//删除pos之后的数据
void SLTEraseAfter(SLTNode* pos)
{assert(pos && pos->next);SLTNode* del = pos->next;//让pos指向它之后的之后的数据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;
}
六、使用演示
不完全演示:
#include "SList.h"void SListTest1()
{//指针要初始化为空SLTNode* plist = NULL;//尾插SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPushBack(&plist, 5);printf("尾插:");SLTPrint(plist);//头插SLTPushFront(&plist, 11);SLTPushFront(&plist, 12);SLTPushFront(&plist, 13);SLTPushFront(&plist, 14);SLTPushFront(&plist, 15);printf("头插:");SLTPrint(plist);//尾删SLTPopBack(&plist);printf("尾删:");SLTPrint(plist);//头删SLTPopFront(&plist);printf("头删:");SLTPrint(plist);//指定位置之前插入数据SLTInsert(&plist, SLTFind(plist, 2), 66);printf("在2的前面插入66:");SLTPrint(plist);//删除指定位置数据SLTErase(&plist, SLTFind(plist, 66));printf("删除66:");SLTPrint(plist);//销毁链表SListDestroy(&plist);printf("销毁:");SLTPrint(plist);
}int main()
{SListTest1();return 0;
}
运行结果:

总结
以上就是本文的全部内容,感谢支持。
相关文章:
数据结构之链表(1),单链表
目录 前言 一、什么是链表 二、链表的分类 三、单链表 四、单链表的实现 五、SList.c文件完整代码 六、使用演示 总结 前言 本文讲述了什么是链表,以及实现了完整的单链表。 ❤️感谢支持,点赞关注不迷路❤️ 一、什么是链表 1.概念 概念:链…...
如何构建鲁棒高性能 Prompt 的方法?
你好,我是三桥君 在当今时代,利用大型语言模型如ChatGPT进行文本生成和交互已成为一种趋势。然而,要充分发挥这些模型的能力,尤其是在生产环境中,我们需要精心设计和优化我们的提示词(prompt)。…...
基于Springboot+微信小程序 的高校社团管理小程序(含源码+数据库+lw)
1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 系统定…...
Vue 响应式监听 Watch 最佳实践
一. 前言 上一篇文章我们学习了 watch 的基础知识,了解了它的基本使用方法及注意事项,本篇文章我们继续了解在Vue 中 响应式监听 watch 的妙用。了解 watch 的基础使用请参考上一篇文章: 详解 Vue 中 Watch 的使用方法及注意事项https://bl…...
md编辑器语法
这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…...
RabbitMQ常用管理命令及管理后台
RabbitMQ管理命令 1、用户管理1.1、新增一个用户1.2、查看当前用户列表1.3、设置用户角色1.4、设置用户权限1.5、查看用户权限 2、RabbitMQ的web管理后台2.1、查看rabbitmq 的插件列表2.2、启用插件2.3、禁用插件2.4、访问RabbitMQ的web后台2.4、通过web页面新建虚拟主机 ./rab…...
从准备面试八股文,感悟到技术的本质
工作前几年听说过,大学最重要的几门课其实是数据结构和算法、操作系统、计算机组成原理、计算机网络。 初听时不以为然,感觉没什么用。 近期准备面试八股文得到了一些感悟。这句话随着工作年限和对程序的理解越来越深入,含金量越来越高。 最…...
云手机的默认ip地址是什么
云手机(Cloud Phone)是一种基于云计算技术的虚拟手机,它可以在云端运行,使用户能够通过互联网访问手机应用和服务。云手机的IP地址通常取决于以下几个因素: 1. 云服务提供商 不同的云服务提供商(如AWS、G…...
对接阿里asr和Azure asr
1:对接阿里asr 1.1:pom <dependency><groupId>com.alibaba.nls</groupId><artifactId>nls-sdk-recognizer</artifactId><version>2.2.1</version> </dependency>1.2:生成token package c…...
未来数字世界相关技术、应用:AR/VR/MR;数字人、元宇宙、全息显示
一、AR/VR/MR 增强现实(AR)、虚拟现实(VR)和混合现实(MR)是三种不同的技术,它们都旨在增强用户对现实世界的感知和交互体验。以下是它们的详细介绍: 增强现实(AR) 增强现实(Augmented Reality, AR) 是一种将虚拟信息叠加到现实世界中的技术。通过AR技术,用户可…...
在 Java 中提供接口方法而不是实现接口
问题 我正在阅读有关Java中的接口的文章。其中提到我们必须实现compareTo方法才能在ArrayList容器上调用sort,例如Employee类应该实现 Comparable接口。 后面解释了为什么Employee类不能简单地提供compareTo方法而不实现Comparable接口?之所以需要接口…...
伪类选择器
一、基本概念 伪类选择器以冒号(:)开头,后面跟着伪类名。它不直接对应DOM中的任何元素,而是用于描述元素的特殊状态或位置。通过使用伪类选择器,可以在不修改HTML文档结构的情况下,为元素添加或修改样式。…...
亚信安全天穹5分钟勒索体检 免费试用今起上线
对于勒索攻击的认知 你是否还停留在“2.0时代”? 勒索攻击无疑是企业面临的最大威胁,2024年上半年,勒索组织数量同步增长超过50%,勒索攻击数量也持续攀升,平均勒索赎金突破520万美元。 当前,勒索攻击治理…...
高校竞赛管理系统的设计与实现
摘 要 如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,往往能解决一些老技术的弊端问题。因为传统高校竞赛管理系统信息管理难度大,容错率低&am…...
物联网行业中通信断线重连现象介绍以及如何实现
01 概述 断线重连是指在计算机网络中,当网络连接遇到异常中断或者断开时,系统会自动尝试重新建立连接,以保证网络通信的连续性和稳定性。这是一种常见的网络通信技术,广泛应用于各种计算机网络场景,包括互联网、局域…...
新手上路:Anaconda虚拟环境创建和配置以使用PyTorch和DGL
文章目录 前言步骤 1: 安装 Anaconda步骤 2: 创建新的 Anaconda 环境步骤 3: 安装最新版本的 PyTorch步骤 4: 安装特定版本的 PyTorch步骤 5: 安装最新版本的 DGL步骤 6: 安装特定版本的 DGL步骤 7: Pycharm中使用虚拟环境解释器第一种情况:创建新项目第二种情况&am…...
centos7系统安装宝塔面板
1、开始安装 适用系统 Centos/OpenCloud/Alibaba 稳定版9.0.0 urlhttps://download.bt.cn/install/install_lts.sh;if [ -f /usr/bin/curl ];then curl -sSO $url;else wget -O install_lts.sh $url;fi;bash install_lts.sh ed8484bec等待命令执行,安装完成&#…...
汽车总线之----J1939总线
instruction SAE J1939 是由美国汽车工程协会制定的一种总线通信协议标准,广泛应用于商用车,船舶,农林机械领域中,J1939协议是基于CAN的高层协议,我们来看一下两者之间的关系。在J1939 中,物理层和数据链路…...
基于skopt的贝叶斯优化基础实例学习实践
贝叶斯方法是非常基础且重要的方法,在前文中断断续续也有所介绍,感兴趣的话可以自行移步阅读即可: 《数学之美番外篇:平凡而又神奇的贝叶斯方法》 《贝叶斯深度学习——基于PyMC3的变分推理》 《模型优化调参利器贝叶斯优化bay…...
OJ在线评测系统 后端 用策略模式优化判题机架构
判题机架构优化(策略模式) 思考 我们的判题策略可能会有很多种 比如 我们的代码沙箱本身执行程序需要消耗时间 这个时间可能不同的编程语言是不同的 比如沙箱执行Java要额外花费2秒 我们可以采用策略模式 针对不同的情况 定义不同独立的策略 而不是把所有情况全部放在一个i…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
