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

超详细实现单链表的基础增删改查——基于C语言实现

文章目录

  • 1、链表的概念与分类
    • 1.1 链表的概念
    • 1.2 链表的分类
  • 2、单链表的结构和定义
    • 2.1 单链表的结构
    • 2.2 单链表的定义
  • 3、单链表的实现
    • 3.1 创建新节点
    • 3.2 头插和尾插的实现
    • 3.3 头删和尾删的实现
    • 3.4 链表的查找
    • 3.5 指定位置之前和之后插入数据
    • 3.6 删除指定位置的数据和删除指定位置之后的数据
    • 3.7 链表的销毁
  • 4、单链表与顺序表的区别
  • 5、结语
  • 6、 完整实现代码
    • 6.1 头文件SList.h
    • 6.2 SList.c文件

1、链表的概念与分类

1.1 链表的概念

链表是一种在物理结构上不连续、非线性的数据存储结构,但它在逻辑结构上是线性的,这是通过链表中的指针链接次序来实现。
链表结构

1.2 链表的分类

链表有很多种结构,分别带头和不带头,单向和双向,循环和不循环,以上各种情况组合起来就多达222 = 8种链表结构,分别是:

  1. 不带头单向不循环链表
  2. 不带头单向循环链表
  3. 不带头双向循环链表
  4. 不带头双向不循环链表
  5. 带头双向循环链表
  6. 带头单向循环链表
  7. 带头单向不循环链表
  8. 带头双向不循环链表

**单向和双向的区别:**单向的链表每个节点只有一个指向下一个节点的指针,而双向链表每个节点有指向下一个节点的指针,也有指向上一个节点的指针。
单向链表和双向链表
**带头和不带头的区别:**不带头链表的第一个节点也就是头节点,是第一个存储数据的有效节点,而带头链表的头节点则是不存储数据,也被叫做哨兵位,哨兵位后的第一个节点才开始存储有效数据。
带头和不带头
**循环与不循环的区别:**不循环链表的尾节点指向NULL,而循环链表的尾节点指向链表的头节点,构成环。
循环与不循环
上面这么多种结构中,我们最常用的是不带头单向不循环链表(简称单链表)和带头双向循环链表。

2、单链表的结构和定义

2.1 单链表的结构

依据前面所提的关于不带头单向不循环链表的结构,我们可以得出,单链表每个节点有两个变量,一个用来存放数据,一个用来指向下一节点,那么我们就可以根据这个结构来定义我们的单链表。

2.2 单链表的定义

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

其中,我们将int重定义为SLTDataType,是为了后续方便我们修改整个链表的数据类型,将struct SListNode重定义为SLTNode,方便我们后面使用。

3、单链表的实现

3.1 创建新节点

因为后续的插入操作都需要创建新节点,为实现代码复用,避免太多重复代码,我们封装一个用于创建新节点的函数。
实现思路:首先创建一个临时的节点并为其开辟空间(提高代码健壮性可以判断是否申请空间成功),然后将传过来的值赋给创建的节点,再将新节点的指针置空,最后返回这个节点。
具体实现代码如下:

SLTNode* SLTBuyNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}

3.2 头插和尾插的实现

我们给出下面两个函数的声明,分别是实现链表的头插和尾插,注意这里传过去的是二级指针,因为链表头节点本身就是一个一级的结构体指针,我们要修改它的值,就要在这里进行传址操作。

//链表的头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);

链表头插的实现思路:首先我们要判断传进来的指针是否为空,可以使用assert断言,如果为空则直接异常中止,如果不为空则创建新节点,并将新节点的指针指向头节点,再将新节点作为新的头节点。
实现代码:

void SListPushFront(SLTNode** pphead, SListDataType x)
{assert(pphead);SLTNode* newNode = SLTBuyNode(x);newNode->next = *pphead;*pphead = newNode;
}
//链表的尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);

尾插的实现思路:同样,进来先用断言判断pphead是否为空,然后创建新节点,再判断链表是否为空,如果为空,则将新创建的节点作为头节点,然后返回,如果不为空,则需要遍历当前链表,找到尾节点,将尾节点的next指针指向我们的新节点。
代码实现如下:

//链表尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = SLTBuyNode(x);//链表为空if (*pphead == NULL){*pphead = newnode;return;}//链表不为空,找尾节点SLTNode* ptail = *pphead;while (ptail->next){ptail = ptail->next;}ptail->next = newnode;
}

3.3 头删和尾删的实现

函数声明:

//链表的头删
void SLTPopFront(SLTNode** pphead);

头删的实现思路:
头删我们要考虑链表可能存在为空、只有一个节点、有多个节点三种情况,因此我们在用断言判断头节点指针是否为空后要再判断头节点是否为空,再去考虑只有一个节点和多个节点的情况,如果只有一个节点,那么执行完头删后链表为空,我们需要释放头节点的内存,并将头节点置空,然后返回,如果有多个节点,我们则创建一个新的节点,来存放头节点的next指针,也就是第二个节点,再释放头节点的内存(这里就是头删操作),这个时候再将我们创建的新节点赋值给头节点,此时头节点就为原来的第二个节点,完成头删操作。
代码实现如下:

void SLTPopFront(SLTNode** pphead) {assert(pphead);assert(*pphead);//链表只有一个节点if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;return;}SLTNode* newhead = (*pphead)->next;free(*pphead);*pphead = newhead;}
//链表的尾删
void SLTPopBack(SLTNode** pphead);

尾删的实现思路:
同样,我们还是要考虑链表为空、链表只有一个节点、链表有多个节点的情况,链表为空则无法删,链表只有一个节点那么就和头删一样的处理方式,释放头节点,置空然后返回,如果存在多个节点,我们则需要遍历链表找到为尾节点,不同于头删的是,我们在遍历链表找尾节点的时候,要创建一个临时的变量prev来存放尾节点的前一个节点,然后找到尾节点后释放尾节点,并将prev的next指针置空,再赋值给尾节点,此时就完成了尾删操作。
代码实现如下:

void SLTPopBack(SLTNode** pphead) {assert(pphead);//链表不能为空assert(*pphead);//链表只有一个节点if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;return;}SLTNode* ptail = *pphead;SLTNode* prev = NULL;while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;free(ptail);ptail = NULL;
}

3.4 链表的查找

函数声明:

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

查找的实现思路:这个实现的思路比较简单,首先传进来的pphead不能为空,然后遍历整个链表,用每个节点的data和x进行比较,如果相同则返回当前节点,如果遍历完整个链表找不到和x相同的data,则说明不存在这个节点,那么就返回一个NULL。
代码实现如下:

//查找
SLTNode* SLTFind(SLTNode** pphead, SLTDataType x)
{assert(pphead);//遍历链表SLTNode* pcur = *pphead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

将SLTNode* 类型作为查找函数的返回类型,可以方便我们后续指定位置进行操作。

3.5 指定位置之前和之后插入数据

函数声明:

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

指定位置之前插入数据的实现思路:首先我们要考虑传进来的pphead是否为空,删除的节点是否为空,以及链表是否为空的情况,所以一开始要有3次断言,创建一个新节点,然后考虑删除的节点pos正好是头节点的情况,以及pos不是头节点的情况,如果是头节点则直接进行头插操作,不是头节点我们则需要遍历链表找pos节点,这里的循环条件是当前节点prev的下一个节点不为pos,那么循环终止时,当前节点prev就应该是pos的前一个节点,此时我们再让新节点的next指针指向pos,让prev的next指针指向新节点,完成pos前插入数据的操作。
代码实现如下:

//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) {assert(pphead);assert(pos);assert(*pphead);SLTNode* newnode = SLTBuyNode(x);//pos刚好是头节点if (pos == *pphead){//头插SLTPushFront(pphead, x);return;}//pos不是头节点的情况SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}newnode->next = pos;prev->next = newnode;
}
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);

指定位置之后插入数据的实现思路:
可以看到这个函数的参数只有pos节点和插入数据x,也就是说不需要对整个链表进行操作,具体是如何实现的呢,同样的,我们需要先用断言判断pos节点是否为空,然后创建一个新节点存放插入数据x,我们要在pos节点之后插入数据,这个操作会同时影响到pos和pos的下一个节点,也就是pos->next,我们只需要让新节点的next指针指向pos的下一个节点pos->next,再让pos的next指针指向新节点,即可完成pos后插入数据的操作。
代码实现如下:

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

3.6 删除指定位置的数据和删除指定位置之后的数据

//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);

实现思路:首先我们还是要对pphead、*pphead以及pos进行断言,然后考虑pos节点是否为头节点的情况,如果为头节点,那么我们直接进行头删操作即可,如果不是,我们就要遍历链表,找到pos的前一个节点prev,让prev的next指针指向pos的下一个节点pos->next,再释放pos节点的内存,将pos置空,至此完成删除pos节点操作。
代码实现如下:

//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos) {assert(pphead);assert(*pphead);assert(pos);//pos是头节点if (pos == *pphead){//头删SLTPopFront(pphead);return;}SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;
}
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);

实现思路:我们要考虑两种情况,pos节点是否为空,以及pos是否为尾节点(如果pos是尾节点就没有删除pos之后节点的说法了),因此我们需要对pos和pos->next进行断言。我们创建一个临时的节点del存放pos的下一个节点,也就是我们要删除的节点,然后让pos的next指针指向del的下一个节点,也就是pos的下下个节点,再释放del,将del置空,这样就完成对pos后的节点删除的操作了。
代码实现:

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

3.7 链表的销毁

函数声明:

//销毁链表
void SLTDestroy(SLTNode** pphead);

实现思路:首先还是要对pphead和pphead进行断言,然后就是遍历链表对每个节点依次释放内存,最后要将头节点pphead置空。
代码实现如下:

//销毁链表
void SLTDestroy(SLTNode** pphead) {assert(pphead);assert(*pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

4、单链表与顺序表的区别

单链表和顺序表都是线性表,但顺序表在物理结构上一定连续,因为顺序表的底层结构是数组,数组的存储就是一块连续的空间,而单链表在物理结构上不一定连续,为什么说不一定的,因为每个节点的地址都是随机分配的,无法确定。顺序表相对于单链表而言,它因为底层结构是数组,所以能够做到随机访问,而链表随机访问一个节点都要进行遍历,效率较低,但顺序表插入和删除数据需要对整个数组的元素进行搬移,而链表则只需要修改指针指向,二者各有各的优缺点,这也就给他们带来了不同的应用场景,但你需要频繁访问存储的数据时,可以考虑顺序表作为底层结构,当你需要频繁的删除插入操作时,链表就更加符合你的需求。所以说存在即合理,每个数据结构都有他们的优势和缺陷,没有绝对的谁好谁差的区分。我们要做到的是了解、熟悉每一个数据结构,在未来遇到各种应用场景时,能够根据不同的需求选择最合适的数据结构。

5、结语

这篇文章就讲到这里了,单链表相关功能的测试,大家就自己去尝试一下,如果存在什么错误和纰漏的地方,请及时指出,一定改正,数据结构这部分确实是代码量最多的一部分,不仅要学数据结构,还有各种各样的算法,需要不断地加以练习,后面会开个新专栏用来记录我的刷题,努力学习,共同进步。最后附上完整的代码,希望这篇文章能够给你带来帮助。

6、 完整实现代码

6.1 头文件SList.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#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 SLTPushFront(SLTNode** pphead, SLTDataType x);
void SLTPushBack(SLTNode** pphead, SLTDataType x);//链表的头删和尾删
void SLTPopFront(SLTNode** pphead);
void SLTPopBack(SLTNode** pphead);//查找
SLTNode* SLTFind(SLTNode** pphead, SLTDataType x);//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead,SLTNode* pos, SLTDataType x);//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);//销毁链表
void SLTDestroy(SLTNode** pphead);

6.2 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* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}//链表尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = SLTBuyNode(x);//链表为空if (*pphead == NULL){*pphead = newnode;return;}//链表不为空,找尾节点SLTNode* ptail = *pphead;while (ptail->next){ptail = ptail->next;}ptail->next = newnode;
}//链表头插
void SLTPushFront(SLTNode** pphead, SLTDataType x) {assert(pphead);SLTNode* newnode = SLTBuyNode(x);//链表为空//链表不为空newnode->next = *pphead;*pphead = newnode;
}//链表的头删和尾删
void SLTPopFront(SLTNode** pphead) {assert(pphead);assert(*pphead);//链表只有一个节点if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;return;}SLTNode* newhead = (*pphead)->next;free(*pphead);*pphead = newhead;}
void SLTPopBack(SLTNode** pphead) {assert(pphead);//链表不能为空assert(*pphead);//链表只有一个节点if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;return;}SLTNode* ptail = *pphead;SLTNode* prev = NULL;while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;free(ptail);ptail = NULL;
}//查找
SLTNode* SLTFind(SLTNode** pphead, SLTDataType x)
{assert(pphead);//遍历链表SLTNode* pcur = *pphead;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);assert(*pphead);SLTNode* newnode = SLTBuyNode(x);//pos刚好是头节点if (pos == *pphead){//头插SLTPushFront(pphead, x);return;}//pos不是头节点的情况SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}newnode->next = pos;prev->next = newnode;
}//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos) {assert(pphead);assert(*pphead);assert(pos);//pos是头节点if (pos == *pphead){//头删SLTPopFront(pphead);return;}SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;
}//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTBuyNode(x);newnode->next = pos->next;pos->next = newnode;
}//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos)
{assert(pos);assert(pos->next);SLTNode* del = pos->next;pos->next = pos->next->next;free(del);del = NULL;}//销毁链表
void SLTDestroy(SLTNode** pphead) {assert(pphead);assert(*pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

相关文章:

超详细实现单链表的基础增删改查——基于C语言实现

文章目录 1、链表的概念与分类1.1 链表的概念1.2 链表的分类 2、单链表的结构和定义2.1 单链表的结构2.2 单链表的定义 3、单链表的实现3.1 创建新节点3.2 头插和尾插的实现3.3 头删和尾删的实现3.4 链表的查找3.5 指定位置之前和之后插入数据3.6 删除指定位置的数据和删除指定…...

分布式光纤测温技术让森林火灾预警快人一步

2025年春季&#xff0c;多地接连发生森林火灾&#xff0c;累计过火面积超 3万公顷。春季历来是森林草原火灾易发、多发期&#xff0c;加之清明节已到来&#xff0c;生产生活用火活跃&#xff0c;民俗祭祀用火集中&#xff0c;森林火灾风险进一步加大。森林防火&#xff0c;人人…...

判断链表是否为环(Java版本自己用)

141. 环形链表 核心代码版本&#xff1a; public class Solution {public boolean hasCycle(ListNode head) {if (head null) {return false;}ListNode slow head;ListNode fast head.next;while (fast ! null && fast.next ! null) {if (slow fast) {return true…...

leetcode 516. Longest Palindromic Subsequence

题目描述&#xff1a; 代码&#xff1a; class Solution { public:int longestPalindromeSubseq(string s) {int n s.size();//i<j,dp[i][j]表示s[i,j]的最长回文子串的长度,按照这个定义dp[0][n-1]就是答案,i>j的dp[i][j]不定义vector<vector<int>> dp(n,…...

关于敏感文件或备份 安全配置错误 禁止通过 URL 访问 Vue 项目打包后的 .gz 压缩文件

要禁止通过 URL 访问 Vue 项目打包后的 .gz 压缩文件&#xff08;如 sc.6abb69d9.css.gz&#xff09;或其他敏感文件&#xff0c;可以通过 Nginx 配置和 Tomcat 配置双重防护来实现。以下是具体解决方案&#xff1a; 方法 1&#xff1a;通过 Nginx 配置禁止访问 .gz 文件 在 N…...

Linux系统启动全流程解析:从BIOS到用户登录

摘要 深度解析Linux系统启动五阶段&#xff1a;内核加载→init进程初始化→系统服务启动→终端创建→用户登录&#xff0c;涵盖SysV/Systemd差异及运行级别管理&#xff0c;提供故障排查指南。 一、启动流程全景概览 Linux系统启动过程严格遵循5个阶段顺序执行&#xff0c;每…...

unity动态骨骼架设+常用参数分享(包含部分穿模解决方案)

Unity骨骼物理模拟插件Dynamic Bone Dynamic Bone 可用于对角色的骨骼&#xff08;bones&#xff09;或者铰链系统&#xff08;joints&#xff09;施加物理效果。 物理效果可以使得游戏角色的头发、衣服、胸部或者是其他的任何部位&#xff0c;都可以以近似真实的状态运动。 …...

【云原生】k8s集群部署最新版ELFK日志采集平台

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Golang开…...

基于瑞芯微RK3576国产ARM八核2.2GHz A72 工业评估板——ROS2系统使用说明

前 言 本文主要介绍创龙科技TL3576-MiniEVM评估板演示基于Ubuntu的ROS系统(版本:ROS2 Foxy)使用说明,包括镜像编译、镜像替换,以及ROS系统测试的方法。适用开发环境如下。 Windows开发环境:Windows 10 64bit Linux虚拟机环境:VMware16.2.5、Ubuntu22.04.5 64bit U-B…...

android studio sdk unavailable和Android 安装时报错:SDK emulator directory is missing

md 网上说的都是更换proxy代理什么的&#xff0c;还有一些二其他乱七八糟的&#xff0c;根本没用&#xff0c;感觉很多就是解决不了问题&#xff0c;还贼多贼一致&#xff0c;同质化&#xff0c;感觉很坑人&#xff0c;让人觉得他们和我的一样的&#xff0c;大家都是按他们说的…...

qemu构建arm环境(AI生成)

要编译 qemu-system-arm&#xff0c;你需要安装一些依赖库&#xff0c;配置编译环境&#xff0c;并执行编译过程。以下是一般步骤&#xff0c;适用于大多数基于Linux的系统。 1. 安装依赖 首先&#xff0c;你需要安装一些必要的开发工具和库。你可以使用包管理器来安装这些依…...

10天学会嵌入式技术之51单片机-day-4

第十二章 中断系统 中断系统是单片机用于处理外部紧急事件的一种机制。中断系统工作的大致流程如下 图所示&#xff1a;当 CPU 正在处理某项任务时&#xff0c;外部发生了某个紧急事件&#xff0c;此时 CPU 会暂停当前 的工作&#xff0c;转而去处理这个紧急事件&#xff0c;处…...

spark—SQL3

连接方式 内嵌Hive&#xff1a; 使用时无需额外操作&#xff0c;但实际生产中很少使用。 外部Hive&#xff1a; 在虚拟机下载相关配置文件&#xff0c;在spark-shell中连接需将hive-site.xml拷贝到conf/目录并修改url、将MySQL驱动copy到jars/目录、把core-site.xml和hdfs-sit…...

CENTOS 7 安装VNC

一、VNC简介 VNC&#xff08;Virtual Network Computing&#xff09;&#xff0c;为一种使用RFB协议的屏幕画面分享及远程操作软件。此软件借由网络&#xff0c;可发送键盘与鼠标的动作及即时的屏幕画面。 VNC与操作系统无关&#xff0c;因此可跨平台使用&#xff0c;例如可用…...

第42讲:走进智慧农业的“感知神经系统”——农田遥感 + 边缘计算的融合实践

目录 ✨一、为什么要融合遥感与边缘计算? 🧪二、典型应用场景案例 ✅ 案例 1:棉花田的智能水分监测系统 ✅ 案例 2:水稻纹枯病自动识别与预警系统 💻三、关键技术框架与实现思路 🚦 1. 系统架构流程图: 📦 2. 模型部署建议: 💡四、未来发展趋势展望 �…...

Dify忘记管理员密码,重置的问题

今天本地win10电脑&#xff0c;使用源码启动dify&#xff0c;忘记了管理员账号和密码&#xff0c;于是网上查找解决办法。 1.有的网上资料说是去数据库删除用户表&#xff0c;于是进入数据库&#xff1a; docker exec -it docker-db-1 psql -U postgres -d dify 找到postgre…...

C#—Lazy<T> 类型(延迟初始化/懒加载模式)

C# 的 Lazy<T> 类型 Lazy<T> 是 C# 中的一个类&#xff0c;用于实现延迟初始化&#xff08;懒加载&#xff09;模式。它提供了一种线程安全的方式来延迟创建大型或资源密集型对象&#xff0c;直到第一次实际需要时才进行初始化。 主要特点 延迟初始化&#xff1a…...

unity打包安卓时的签名文件jks转换keystore

前言 unity打包安卓时需要的签名文件格式默认是keystore&#xff0c;而有时我们拿到的是jks格式的签名文件&#xff0c;就需要把jks格式文件转换成keystore格式文件。 其实在windows下也可以不转换&#xff0c;在选择签名文件的文件选择框时&#xff0c;把文件扩展名筛选项&a…...

Android audio_policy_configuration.xml加载流程

目录 一、audio_policy_configuration.xml文件被加载流程 1、AudioPolicyService 创建阶段 2、createAudioPolicyManager 实现 3、AudioPolicyManager 构造 4、配置文件解析 loadConfig 5、核心解析逻辑 PolicySerializer::deserialize 二、AudioPolicyConfig类解析 1、…...

AOSP Android14 Launcher3——远程窗口动画关键类SurfaceControl详解

在 Launcher3 执行涉及其他应用窗口&#xff08;即“远程窗口”&#xff09;的动画时&#xff0c;例如“点击桌面图标启动应用”或“从应用上滑回到桌面”的过渡动画&#xff0c;SurfaceControl 扮演着至关重要的角色。它是实现这些跨进程、高性能、精确定制动画的核心技术。 …...

iframe下系统访问跨域问题解决办法

问题描述&#xff1a;iframe下嵌入web页面&#xff0c;访问后端接口跨域&#xff0c;导致接口调不通。 产生原因&#xff1a;iframe下&#xff0c;web端访问后端接口时&#xff0c;会优先向后端发送请求方法为OPTIONS的预检测请求&#xff0c;该请求调用不通&#xff0c;导致真…...

Kafka 如何理解Kafka的高可用

一、Kafka高可用核心思想&#xff1a;备胎的自我修养 核心口诀&#xff1a;“别把鸡蛋放在一个篮子里&#xff0c;除非你他妈有100个篮子&#xff01;” Kafka的高可用设计&#xff0c;本质上就是一场**“分布式备胎大战”**。它的核心逻辑是&#xff1a; “老子不信任任何单…...

11-DevOps-Jenkins Pipeline流水线作业

前面已经完成了&#xff0c;通过在Jenkins中创建自由风格的工程&#xff0c;在界面上的配置&#xff0c;完成了发布、构建的过程。 这种方式的缺点就是如果要在另一台机器上进行同样的配置&#xff0c;需要一项一项去填写&#xff0c;不方便迁移&#xff0c;操作比较麻烦。 解…...

C++学习之游戏服务器开发十一DOCKER的基本使用

目录 1.多实例部署方案 2.容器的概念 3.docker初识 4.docker仓库 5.docker镜像 6.docker容器 7.docker和虚拟机的区别 8.docker命令解释 9.dockerfile构建镜像 10.离线分发镜像 1.多实例部署方案 redis 命令&#xff08; redis-cli XXXX &#xff09; set key value:…...

docker学习笔记2-最佳实践

一、在容器中启动mysql的最佳实践 &#xff08;一&#xff09;查找目录 1、mysql的配置文件路径 /etc/mysql/conf.d 2、mysql的数据目录 /var/lib/mysql 3、环境变量 4、端口 mysql的默认端口3306。 &#xff08;二&#xff09;启动命令 docker run -d -p 3306:3306 …...

【TeamFlow】4.2 Yew库详细介绍

Yew 是一个用于构建高效、交互式前端 Web 应用程序的现代 Rust 框架&#xff0c;它借鉴了 React 和 Elm 等框架的设计理念&#xff0c;同时充分利用 Rust 的语言特性。 核心特性 基于组件的架构 Yew 采用组件化开发模式&#xff0c;类似于 React: 组件是可重用的 UI 构建块 …...

第六章.java集合与泛型

文章目录 1.集合框架1. Collection 接口存储一组不唯一,无序的对象2. Set接口存储一组唯一,无序的对象3. Map接口存储一组键值对象,提供key到value的映射 2.封装3.练习题 1.集合框架 java集合框架提供了一套性能优良,使用方便的接口和类,它们位于java.util中 1. Collection 接…...

elastic/go-elasticsearch与olivere/elastic

在 Go 语言中&#xff0c;与 Elasticsearch 交互的客户端库有多种选择&#xff0c;其中 github.com/elastic/go-elasticsearch/v8 和 github.com/olivere/elastic/v7 是两个常用的库。这两个库的功能和用途有一些差异&#xff0c;以下是它们的详细对比&#xff1a; 1. github.c…...

MYSQL之基础认识(卸载安装登录, 基本概念)

一. 卸载安装和登录 卸载 MYSQL 1. 查看有无mysql服务正在运行: ps ajx | grep mysql 2. 查看到 mysql 的服务名称: systemctl list-units --typeservice | grep mysql 3. 关闭 mysql 服务 4. 卸载 dpkg -l | grep mysql | awk {print $2} | xargs sudo apt remove --purg…...

Sentinel源码—7.参数限流和注解的实现一

大纲 1.参数限流的原理和源码 2.SentinelResource注解的使用和实现 1.参数限流的原理和源码 (1)参数限流规则ParamFlowRule的配置Demo (2)ParamFlowSlot根据参数限流规则验证请求 (1)参数限流规则ParamFlowRule的配置Demo 一.参数限流的应用场景 二.参数限流规则的属性 …...

JAVA:利用 Apache Tika 提取文件内容的技术指南

1、简述 Apache Tika 是一个强大的工具,用于从各种文件中提取内容和元数据。📄Tika 支持解析文档、📸图像、🎵音频、🎥视频文件以及其他多种格式,非常适合构建🔍搜索引擎、📂内容管理系统和📊数据分析工具。 样例代码:https://gitee.com/lhdxhl/springboot-…...

SVM(支持向量机)

SVM&#xff08;支持向量机&#xff09; 原理 SVM的核心目标是找到一个最大化分类间隔的超平面&#xff0c;将不同类别的样本分隔开。其原理可分为三部分&#xff1a; 线性可分情况 通过硬间隔最大化确定超平面&#xff0c;确保所有样本正确分类且间隔最大间隔定义为超平面到最…...

Spark,hadoop的组成

&#xff08;一&#xff09;Hadoop的组成 对普通用户来说&#xff0c; Hadoop就是一个东西&#xff0c;一个整体&#xff0c;它能给我们提供无限的磁盘用来保存文件&#xff0c;可以使用提供强大的计算能力。 在Hadoop3.X中&#xff0c;hadoop一共有三个组成部分&#…...

数据结构中的各种排序

排序之冒泡排序 原理&#xff1a;比较相邻的元素&#xff0c;将大的元素放右边&#xff0c;小的元素放左边。每一趟排的最后的元素一定是最大的元素&#xff0c;所有下一趟需要排列的元素可减少一个 public int[] bubbleSort(int[] attr) {for (int i 0; i < attr.length…...

Android 中实现 GIF 图片动画

在 Android 中&#xff0c;ImageView 从 Android 9.0&#xff08;API 级别 28&#xff09; 开始原生支持 GIF 动画&#xff0c;通过 AnimatedImageDrawable 类实现。在之前的版本中&#xff0c;ImageView 并不支持直接播放 GIF 动画&#xff0c;只能显示 GIF 的第一帧。 一、 …...

linux安装mysql数据库

1.判断系统是多少位的 file /sbin/init2.下载linux安装包 5.7.25.64位安装包链接&#xff1a;https://pan.baidu.com/s/13vFuRikwJaI96K0AmUQXzg提取码&#xff1a;ga7h其他版本安装 去官网下载&#xff1a;https://dev.mysql.com/downloads/mysql/3.创建mysql文件夹 mkdir /…...

基于SA模拟退火算法的车间调度优化matlab仿真,输出甘特图和优化收敛曲线

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于SA模拟退火算法的车间调度优化matlab仿真,输出甘特图和优化收敛曲线。输出指标包括最小平均流动时间&#xff0c;最大完工时间&#xff0c;最小间隙时间。 2…...

uniapp云打包针对谷歌视频图片权限的解决方案

谷歌在24年底推出把图片和视频细分为两个权限&#xff0c;uniapp使用uni.chooseImage云打包默认图片视频为一个权限,不符合谷歌要求会被下架 解决方法&#xff0c;在项目根目录下新建AndroidManifest.xml移除不必要的权限 <?xml version"1.0" encoding"utf…...

DSRAM介绍

DSRAM&#xff08;双端口静态随机存储器&#xff09;介绍 1. 基本概念 DSRAM&#xff08;Dual-Port Static Random Access Memory&#xff09;是一种双端口SRAM&#xff0c;支持两个独立的读写接口&#xff0c;允许两个设备&#xff08;如CPU、DMA、FPGA&#xff09;同时访问…...

【仿Mudou库one thread per loop式并发服务器实现】HTTP协议模块实现

HTTP协议模块实现 1. Util模块2. HttpRequest模块3. HttpResponse模块4. HttpContext模块5. HttpServer模块 1. Util模块 这个模块是一个工具模块&#xff0c;主要提供HTTP协议模块所用到的一些工具函数&#xff0c;比如url编解码&#xff0c;文件读写…等。 #include "s…...

教育行业网络安全:守护学校终端安全,筑牢教育行业网络安全防线!

教育行业面临的终端安全问题日益突出&#xff0c;主要源于教育信息化进程的加速、终端设备多样化以及网络环境的开放性。 以下是教育行业终端安全面临的主要挑战&#xff1a; 1、设备类型复杂化 问题&#xff1a;教育机构使用的终端设备包括PC、服务器等&#xff0c;操作系统…...

【网工第6版】第5章 网络互联②

目录 ■ IPV6 ▲ IPV6报文格式 ◎ IPV6扩展报头&#xff08;RFC2460&#xff09; ◎ IPv6相关协议 ▲ IPV6地址分类 ◎ IPv6地址基础 ◎ IPv6地址举例 ◎ IPv6地址分类 ◎ 特殊地址对比IPv4 vs IPv6 ▲ 过渡技术 本章重要程度&#xff1a;☆☆☆☆☆ ■ IPV6 与IPv4…...

ASP.NET Core 分层项目中EFCore的使用

文章目录 前言一、核心二、项目分层结构1&#xff09;安装 NuGet 包Web 项目InfrastructureLibrary项目 2&#xff09;领域模型和仓储接口 (Domain 层)3&#xff09;基础设施层实现 (Infrastructure 层)4&#xff09;应用层服务 (Application 层)5&#xff09;Web API 配置6&am…...

.net core 中directory , directoryinfo ,file, fileinfo区别,联系,场景

一、类定义及核心功能 ‌Directory类‌ ‌类型‌&#xff1a;静态类 ‌功能‌&#xff1a;提供目录操作的静态方法&#xff0c;包括创建、删除、移动目录&#xff0c;以及获取子目录或文件列表等。例如Directory.CreateDirectory()、Directory.GetFiles()。 ‌适用场景‌&…...

jvm-获取方法签名的方法

在Java中&#xff0c;获取方法签名的方法可以通过以下几种方式实现&#xff0c;具体取决于你的需求和使用场景。以下是详细的介绍&#xff1a; 1. 使用反射 API Java 提供了 java.lang.reflect.Method 类来获取方法的相关信息&#xff0c;包括方法签名。 示例代码&#xff1a…...

three.js中的instancedMesh类优化渲染多个同网格材质的模型

three.js小白的学习之路。 在上上一篇博客中&#xff0c;简单验证了一下three.js中的网格共享。写的时候就有一些想法&#xff0c;如果说某个场景中有一万棵树&#xff0c;这些树共享一个geometry和material&#xff0c;有没有好的办法将其进行一定程度上的渲染优化&#xff0…...

2025年一站式AI创作平台主要功能介绍及使用教程

在当今迅速发展的数字时代&#xff0c;人工智能&#xff08;AI&#xff09;已成为推动创新和提升工作效率的关键工具。今天给大家分享一个全面的一站式AIGC内容创作平台&#xff0c;对其主要功能及使用教程进行讲解&#xff0c;旨在帮助用户显著提升工作和学习效率。无论您需要…...

YOLO11改进,尺度动态损失函数Scale-based Dynamic Loss,减少标签不准确对损失函数稳定性的影响

在目标检测领域,标签噪声与尺度敏感问题始终是制约模型性能提升的"阿喀琉斯之踵"。2025年CVPR最佳论文提出的尺度动态损失函数(Scale-based Dynamic Loss, SDL),通过构建自适应损失调节机制,不仅实现了对YOLOv11检测精度的指数级提升,更重新定义了损失函数的设…...

<项目代码>YOLO小船识别<目标检测>

项目代码下载链接 YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0…...

我用deepseek做了一个提取压缩文件夹下pdf和word文件工具

由于最近需要把大量的压缩文件的pdf和word文件统一复制到一个文件夹中。 我们一般正常操作方式的是把一个压缩文件一个一个解压&#xff0c;然后在把一个的解压好的文件夹下文件复制到另外一个文件夹中。 这个也需太繁琐了&#xff0c;从以往统计的需要花费两个小时间&#x…...