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

【数据结构】励志大厂版·初阶(复习+刷题)单链表

前引:此篇文章作为小编复习的记录,将快速回忆单链表的知识点,讲解单链表增删查找的实现,每个细节之处要注意的地方,解释为何这样设计。文章末尾包含了单链表算法题, 同样解释详细,借助题目再次巩固知识点,挑战实战应用,喜欢的伙伴们可以作为大家的复习资料,希望可以给个免费的一键三连哦!正文开始~

目录

知识点速览

单链表实现

定义结构体

初始化

开辟节点

尾插 

头插

尾删

头删

 在指定位置之前插入

在指定位置之后插入

链表 OJ(1)

链表OJ(2)

链表OJ(3)

链表OJ(4)

链表OJ(5)


知识点速览

链表较顺序表而言的优点是地址不连续、空间利用率很高,同样属于线性结构,它的空间是一个个结构体,既可以存储数据,也通过指针存储下一个结构体的地址,实现链式结构,如下抽象理解:

单向不带头非循环链表是最简单的一种链表结构,单链表的实现,需要一个头指针指向第一个节点,然后再后面一个接一个的开辟其它节点,这里下面实现的时候再详细说,下面看看经常说的几个链表名词是哈意思:

单链表结束标志:最后一个节点的指针的 next 指向空(NULL)

头指针:一个结构体类型的指针,用来指向链表的第一个节点

头节点:在第一个节点之前再连接一个节点,通常可以称为哨兵节点,如下图:

带头节点:头指针指向头节点

不带头节点:头指针指向第一个节点

逻辑结构:比如这里我们将链表想象成了一节一节的样子,通过指针连接,在实际操作中方便理解

物理结构:比较严谨,链表的物理结构是下面这样的,上一个节点存放下一个节点的地址

拓展:指针指向的是地址,是数据具有多个字节的第一个字节的地址(每个字节都有一个地址)

单链表实现
定义结构体

每个链表的节点空间其实是一个结构体,下面我们构建链表节点空间,这个结构体里面有一个指针(用来指向下一个节点的地址),一个数据域(用来存储数据)

typedef int Plastic;typedef struct List
{//结构体指针struct List* next;//数据域Plastic data;
}List;
初始化

我们知道链表有一个头指针(结构体类型),用来指向链表的第一个节点,开始还没有开辟节点,所以它的指向应该是空(NULL)

List* pphead = NULL;

 在整个链表中,我们都需要注意以下几点,小编都帮大家整理出来了!希望可以支持一下小编哦

(1)如果需要改变头指针的位置,就传指针地址,采用二级指针接收

          如果不需要改变头指针的位置,就传指针,采用一级指针接收

          例如:我们头插的时候,需要让头指针指向第一个节点,就需要改变头指针位置

(2)链表不存在:我们判断一个链表存不存在,是看它的头指针存不存在

         链表为空:链表为空不代表链表不存在,只是里面没有插入节点,只有一个头指针指NULL 

         例如:二级指针,pphead==NULL,就表示这个链表不存在;*pphead==NULL,为空链表

开辟节点

这里为了后面方便,考虑到需要多次开辟节点,我们将它单独设置在一个函数,开辟之后根据常规流程,需要判断空间的有效性,其次是需要初始化空间,如下:

//开辟节点
List* Newnode(int data)
{//开辟新节点List* newnode = (List*)malloc(sizeof(List));if (newnode == NULL){printf("开辟失败\n");return NULL;}//初始化节点内容newnode->next = NULL;newnode->data = data;return newnode;
}
尾插 

尾插的步骤是先找尾,再插入。如何找尾?新定义一个指针从链表头部开始一个个遍历,直到它的下一个是空,然后将新开辟的节点连接在这个指针的后面,例如我现在定义了一个指针 cur

为什么判断条件是“  cur->next  != NULL ”?

假设,如果cur!=NULL,那么此时cur已经走到了链表的末尾,也就是“NULL”处,如果接下来要插入新节点,那么表示“NULL->next ==新节点”,这样很明显是错误的方式。下面看尾插结果:

//尾插
void Tail_insert(List** pphead,int data)
{//看链表存不存在assert(pphead);//如果是空链表if (*pphead == NULL){*pphead = Newnode(data);return;}//设置新指针,指向链表开头List* cur = *pphead;//找尾while ((cur->next) != NULL){cur = cur->next;}//找到之后进行插入cur->next = Newnode(data);
}
头插

头插我们不需要去找头,因为头指针所指的节点就是该链表的第一个节点,那么如何头插?如下图

先设置一个指针,让其下一个节点是 pphead 指向原先的第一个节点,然后再更新 pphead 指向

//头插
void Head_insert(List** pphead,int data)
{//判断链表是否存在assert(pphead);//如果是空链表if (*pphead == NULL){*pphead = Newnode(data);return;}//开辟节点List* cur = Newnode(data);//设置前后关系cur->next = *pphead;//更新头指针指向*pphead = cur;
}
尾删

尾删肯定需要先遍历找尾,找尾的条件应该是“cur->next != NULL”

其次还需要找到 cur 的前面一个节点,因为尾删之后,我们需要释放尾部节点,然后尾部前面的节点关系应该是prev->next==NULL。如果未重新确立next的关系,链表就出问题了,如下图理解:

//尾删
void Tail_deletion(List* pphead)
{//判断空链表assert(pphead);List* cur = pphead;List* prev = pphead;//找尾while (cur->next != NULL){prev = cur;cur = cur->next;}//释放free(cur);cur = NULL;//改变尾部关系prev->next = NULL;
}
头删

头删需要先找到当前头指针指向节点的下一个节点,然后对头指针所指向的节点释放,再更新头指针(如果先更新再释放,那么我们就找不到原来的第一个节点了,切记!)

 头删之前

                                                                        头删之后  

//头删
void Head_deletion(List** pphead)
{//判断链表释放存在assert(pphead);//判断空链表assert(*pphead);//如果只有一个节点if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;return;}//先找到第二个节点List* cur = (*pphead)->next;//释放头指针所指向的节点free(*pphead);//更新头指针指向*pphead = cur;
}
 在指定位置之前插入

我们先来看图

这里同尾删一样,需要两个指针,一个找指定位置,一个找指定位置的前一个位置,再来确立next的关系即可。这里不需要去改变头指针的位置,所以使用一级指针就可以!

下面我们来看看插入前与插入后的效果

//在指定位置前插入
void Before_position(List** pphead, int data)
{//判断链表是否存在if (pphead == NULL){return;}//如果是头插if (data == (*pphead)->data){Head_insert(pphead, 5);return;}List* cur = pphead;List* prev = pphead;//找指定位置while (cur->data != data){prev = cur;cur = cur->next;}//插入节点、设置关系prev->next = Newnode(5);prev->next->next = cur;
}
在指定位置之后插入

在指定位置后面插入还是需要另外定义两个指针,先标记cur->next的节点,插入之后方便连接

 插入前插入后的效果展示

//在指定位置后插入
void Behind_position(List* pphead, int data)
{//判断空链表if (pphead == NULL){return;}List* cur = pphead;List* pc = cur->next;//找指定位置while (cur->data != data){cur = cur->next;}//插入cur->next = Newnode(6);//连接(确认next的关系)cur->next->next = pc;
}

链表 OJ(1)

首先跟随小编来分析一下这个题目:此题是将一个无头单向不循环链表进行翻转,原来的首尾全部倒置过来,需要考虑到链表为空、是否越界的情况,以上就是简单的分析,下面进行思路讲解

思路讲解:

(1)反转链表,我们实际上是反转它的箭头指向。这题我们肯定要记录它的下一个节点。因此避免越界,我们应该先进行判断,防止空链表 和 一个节点的情况

(2)排除以上两种特殊情况之后。我们先设置三个变量(下面小编会分析原因!)

分别是 cur ,刚开始指向空;next 指向 head ->next;pc 指向 head。具体指向如图:

 我们通过三个指针的走动来改变链表的指向,例如:

(3)第一轮循环

pc->next = cur;cur = pc ;
pc = next ;
next = next->next;

在开始时,我们的指针是上面这样的。此时改变pc的指向,pc->next=cur,这时第一个节点就已经翻转过来了,如图:

然后改变三个指针的指向     cur=pc  pc=next  next=next->next     第一轮循环就走完了

(4)第二轮循环

执行第一条语句  pc->next=cur  ,就翻转了第二个节点

然后执行  cur=pc  pc=next  next=next->next  就改变了三个指针的指向,如下图:

(5)就这样经过几轮循环

while (next != NULL)
{pc->next = cur;cur = pc ;pc = next ;next = next->next;
}

next已经指向空,循环停止,前面的链表节点已经完全翻转过来了,如下图:

再将剩余的一个节点 翻转过来 pc->next=cur 此时整个链表就翻转过来了

链表OJ(2)

分析题目:这题是找到链表的中间节点,因此最简单的方式就是通过遍历链表,记录链表的长度,最后通过除法确立链表的中间节点,再次遍历返回节点 (此题并不难,暴力解法也是算法哦!)

思维讲解:

我们可以先来遍历链表,记录链表的长度。需要注意空链表的情况

//如果链表为空
if (head == NULL)
{return NULL;
}//遍历链表、记录长度
int count = 0;
struct ListNode* cur = head;while (cur != NULL)
{count++;cur = cur->next;
}

然后通过节点计算中间节点

//计算中间节点
count = count / 2 + 1;

再次遍历节点,通过  count  的减减来返回节点

//再次找中间节点,并返回
cur = head;
while (cur != NULL)
{count--;if (count == 0){break;}cur = cur->next;
}
return cur;

链表OJ(3)

 题目分析:

(1)此题是合并两个有序的链表,方法与“合并两个数组”有异曲同工之妙,但是这里不用创建新的链表,因为题目已经形成了两个链表,我们只需要创建一个头指针改变链表节点的指向就行了

例如:将 List2 剩余的节点移给 pphead ,并不用去创建新的链表

(2)初始化时我们设置2个指针 分别是  pphead  cur,用来组成新的链表,开始时二者都为空

(3)如果存在某个链表是空,或者都为空。那么就直接返回,这是两种特殊情况

 if(list1==NULL){return list2;}if(list2==NULL){return list1;}

 (4)现在我们通过找两个链表的较小值,来选取节点进行重新组合。因为开始pphead  cur 是空,所以需要先处理这种情况,第一次循环找到的较小值应该更新pphead cur,例如:

 if(pphead==NULL){pphead = list1;cur=pphead;}

(5)随后根据找较小值,进行组合就行了,记得更新对应的指针。直到二者有一个为空就结束

 while(list1 != NULL && list2 != NULL){//如果list1的当前节点小if(list1->val <= list2->val){if(pphead==NULL){pphead = list1;cur=pphead;}else{cur->next=list1;cur=cur->next;}list1=list1->next;}//如果list2的当前节点小else{if(pphead==NULL){pphead=list2;cur=pphead;}else{cur->next=list2;cur=cur->next;}list2=list2->next;}}

(6)此时可能有某个链表的节点未排完且链表是有序的,根据判断语句,连接到  cur  指针的后面

 //对剩余的链表元素进行连接if(list1 == NULL){cur->next=list2;}if(list2==NULL){cur->next=list1;}

链表OJ(4)

题目分析:

此题是通过判断这个链表是否有循环部分来返回不同的值,我们不能通过遍历去做这题,不然效率特别低,甚至做不出来,这里小编直接告诉大家一种算法:双指针(快慢指针),咱们先按照这个思路来直接解题,先不考虑如何优化!但是此题真正需要了解的是后面的证明,因为很多面试题是问如何证明二者是否相遇(证明在最后哦!)小编先来讲如何实现,再来解决推理证明问题!

思维分析:

快慢指针是通过两个指针的移动速度不同,来判断二者是否相遇返回不同的值

例如:现在有两个指针curprevprev的速度是cur的两倍。如果链表有环,那么当prev进入环内,cur也会因为与prev的速度不同被prev追赶,二者终会相遇,此时链表有环;如果链表没有环,那么prev就会走到空(NULL),得出链表没有环。下面是图示,已助理解!

(1)如果链表为空,直接返回false

         否则设置两个结构体指针 prev curprev的移动速度是cur的两倍

(2)循环的条件应该是prev不为空,否则直接返回false。那么如何实现快慢呢?

         最简单的方法就是通过次数,比如prev动一次就记录一次,动了两次就轮到cur走一次,然             后重新记录次数。大家可以看下面的代码来理解,小编已经全部注释了!

int count=0;
while(prev){//如果prev动了两次,就轮到cur走了if(count==2){cur=cur->next;//如果cur动的时候遇到了prev,就直接返回trueif(cur==prev){return true;}//重新记录prev动的次数count=0;}else{//prev动一次,就记录一次count++;prev=prev->next;//如果prev遇到了cur,直接返回trueif(prev==cur){return true;}}}
return false;

代码优化:

直接控制指针的next来实现走动次数,减少了冗杂的条件判断(下面主要是优化了循环部分

bool hasCycle(struct ListNode *head) 
{//如果链表为空if(head==NULL){return false;}//设置两个链表指针struct ListNode* prev=head;struct ListNode* cur=head;while(prev && prev->next)      //     优化{//cur每次走一步,prev每次走两步cur=cur->next;prev=prev->next->next;//如果相遇if(prev==cur){return true;}}return false;
}

证明推理:

问:既然一个走一步,一个走两步可以相遇;那么一个走一步,一个走三步呢?以此类推,请证明

首先一个走一步,一个走二步,即二者只差一个单位是一定可以追上的,推理如下:

假如当 cur 进入循环,prev 此时也在循环内,二者相差 N ,如图:

此时 prev 走两步,cur 走一步,二者相差 N-1 ;prev 再走两步, cur 再走一步,二者相差 N-2,那么推下去,N一定会等于 0,也就是二者一定相遇 

如果一个走一步,一个走三步呢?是可能永远追不上的,推理如下:

还是当 cur 进入循环,prev 与 cur 相差 N

此时 prev 走三步,cur 走一步,二者相差 N-2 ;prev 再走三步,cur再走一步,相差 N-4;继续推下去,如果 N 是偶数,那么会相遇距离为 0 ;如果 N 是奇数,二者最终会是 -1 ,即 prev  超过了cur ,为下面这个场景:

此时 二者相距 C-1(C为圆周长)。prev 再走三步,cur 再走一步,二者相距 3,推理下去,当二者相距 N ,随着二者之间永远相差 2 个单位,就一直无法相遇。其它情况同理类推即可 

链表OJ(5)

题目分析:

此题是找到环形链表入环的第一个节点,此题同上题一样,在很多面试题中重点要求证明推理,因此小编会先讲如何证明,当我们知道这个推理过程之后,写起来会很轻松,否则实现真的很难!

证明推理:

我们假设进入 环 之前的距离为 L ,它们在环的  end  位置相遇,其它变量如图所示:

cur 从起点走到相遇位置用的距离:L +  X

prev 从起点到相遇位置用的距离: L + X + C

但是 prev 走的比 cur 快,不可能在第一圈内就与 cur 相遇,肯定是在第一圈之后才追赶到的 cur ,具体的圈数我们是无法确定的,因为这个环可大可小,比如:

所以 prev 正确走的距离应该是 : L + X + N * C (N为未知数,表示几倍的关系)

那么 cur prev 走的路程关系为 :2( L+X)= L + X + N * C(因为prev的速度是cur的两倍)

所以我们得到  :L + X = N * C

                         L + X = ( N - 1 )* C + C

最终我们将整倍的圆周路程不看,可以得到:L +  X  =  C

                                                         也就是: C - X  =  L    (即下面两段红线长度相等)

那么入环点不就是从   相遇位置  与从  起点    开始运动相遇的位置吗?因此我们来实现代码:

(1)先找prev cur二者相遇的位置 (2)设置一个指针pc指向head ,返回curpc相遇点

 

struct ListNode *detectCycle(struct ListNode *head) 
{//判断空链表if(head==NULL){return NULL;}//设置双指针找相遇位置struct ListNode* prev=head;struct ListNode*cur=head;struct ListNode* pc=head;while(prev && prev->next){//prev每次走两步prev=prev->next->next;//cur每次走一步cur=cur->next;//找相遇位置if(cur==prev){//pc指针从开头开始走,cur指针从相遇位置开始走,二者相遇既是环的起点while(pc != cur){cur=cur->next;pc=pc->next;}//此时二者相遇,返回环的起始位置return cur;}}return NULL;
}

好了伙伴们,链表OJ题就到这里结束了!大家千万要弄懂最后两题的证明推理啊!可以关注收藏起来!希望大家面试的时候刚好遇到了今天小编整理的最后两题 ,直接写答案!

相关文章:

【数据结构】励志大厂版·初阶(复习+刷题)单链表

前引&#xff1a;此篇文章作为小编复习的记录&#xff0c;将快速回忆单链表的知识点&#xff0c;讲解单链表增删查找的实现&#xff0c;每个细节之处要注意的地方&#xff0c;解释为何这样设计。文章末尾包含了单链表算法题&#xff0c; 同样解释详细&#xff0c;借助题目再次巩…...

前端面试宝典---参数解构+默认值的面试题

重点要义 对于函数参数要解构&#xff0c;且参数有默认值的&#xff0c;一律用Object.assign的思路去合并参。 看不懂这句话没关系&#xff0c;看下面的例子\ 例子1 function fn ({ x 1, y } { y: 10 }) {console.log(x, y) } fn() // 1 10没有传递实参&#xff0c;你就把{ …...

【开发心得】筑梦上海:项目风云录(16)

目录 代码反面案例 李青与诺基亚的兴衰 并行项目下的利益纠葛与团队协作 未完待续。。。 今天分享的是一个反面案例&#xff0c;也算是一个避坑指南了。 代码反面案例 今天分享的代码是一个反面案例&#xff0c;当时由于项目人员变动频繁&#xff0c;经常是新人看不太懂旧…...

Neovim插件深度解析:mcphub.nvim如何用MCP协议重构开发体验

在AI与工具链深度融合的今天,Neovim 作为现代开发者的生产力工具,正通过插件生态不断突破边界。mcphub.nvim 作为一款基于 MCP(Model Context Protocol) 协议的插件,重新定义了Neovim与智能工具的交互方式。它不仅简化了MCP服务器的集成与管理,更通过直观的UI和生态整合,…...

Qt UDP 通信的详细实现步骤和示例代码

在 Qt 中实现 UDP 通信主要使用 QUdpSocket 类。以下是 UDP 通信的详细实现步骤和示例代码&#xff1a; 一、UDP 通信基础 无连接协议&#xff1a;不需要建立持久连接数据报模式&#xff1a;以独立数据包&#xff08;datagram&#xff09;形式发送适用场景&#xff1a;实时性要…...

(二)Trae 配置C++ 编译

Trae配置c编译 零 CMake 编译C0.1 下载安装0.2 安装设置0.3 三种编译方式(见 下文 一 二 三)0.4 调试 (见 下文四) 一 使用MSVC方式编译1.1 安装编译环境1.2安装插件1.3 设置文件 二 使用GCC方式2.1 安装编译环境2.1.1下载:[MinGw](https://gcc-mcf.lhmouse.com/)2.1.2安装:(以…...

动态规划算法的欢乐密码(一):斐波那契数模型

专栏&#xff1a;算法的魔法世界 个人主页&#xff1a;手握风云 目录 一、动态规划 二、例题讲解 2.1. 第 N 个泰波那契数 2.2. 三步问题 2.3. 使用最小花费爬楼梯 2.4. 解码方法 一、动态规划 动态规划是一种将复杂问题分解为更小的子问题&#xff0c;并利用子问题的解来…...

【FreeRTOS进阶】优先级翻转现象详解及解决方案

【FreeRTOS进阶】优先级翻转现象详解及解决方案 接下来我们聊聊优先级翻转这个经典问题。这个问题在实时系统中经常出现&#xff0c;尤其是在任务较多的场景下&#xff0c;而且问题定位起来比较麻烦。 什么是优先级翻转&#xff1f; 优先级翻转的核心定义很简单&#xff1a;…...

解决 IntelliJ IDEA 项目启动时端口冲突问题

1.问题 Description: The Tomcat connector configured to listen on port 8082 failed to start. The port may already be in use or the connector may be misconfigured. Action: Verify the connectors configuration, identify and stop any process thats listening…...

笔试专题(十一)

文章目录 添加字符&#xff08;暴力枚举&#xff09;题解代码 城市群数量&#xff08;dfs&#xff09;题解代码 判断是不是平衡二叉树&#xff08;递归&#xff09;题解代码 最大子矩阵&#xff08;二维前缀和&#xff09;题解代码 小葱的01串 &#xff08;固定区间大小的滑动窗…...

C++11新增语法:列表初始化

前言&#xff1a; 接下来我们将要讲解&#xff0c;相较于c98&#xff0c;c11中新增的语法以及如何使用~。我们首先来讲解&#xff1a;列表初始化。 下文预告&#xff1a;右值引用和移动语义 C98中传统的{} 在c98中的{}&#xff0c;仅能初始化数组和结构体 #include<iostrea…...

Linux:基础IO---动静态库

文章目录 1. 动静态库前置知识1.1 动静态库知识回顾1.2 什么是动静态库 2. 动静态库2.1 站在库的制作者的角度2.2 站在库的使用者的角度2.3 动态库是怎么被加载的&#xff08;原理&#xff09; 序&#xff1a;上一篇文章我们从认识到理解&#xff0c;从理解到实现场景&#xff…...

从裸仓库到GitLab全解析

Git服务器搭建与使用指南&#xff1a;从裸仓库到GitLab全解析 前言 在团队协作开发中&#xff0c;版本控制系统是必不可少的工具。虽然GitHub提供了优秀的代码托管服务&#xff0c;但企业级项目往往需要更安全的私有化部署方案。本文将手把手教你两种主流的Git服务器搭建方式…...

OzGIS:地理信息分析与处理软件

大家好&#xff0c;今天为大家介绍的软件是OzGIS&#xff1a;一款地理信息分析与处理软件。下面&#xff0c;我们将从软件的主要功能、支持的系统、软件官网等方面对其进行简单的介绍。 OzGIS官网网址为&#xff1a;https://ozgis.sourceforge.io/。 OzGIS是一款开源软件&#…...

PHP异常处理__Throwable

在 PHP 里&#xff0c;Throwable 是一个极为关键的接口&#xff0c;自 PHP 7 起被引入。它为错误和异常处理构建了一个统一的框架。下面会详细介绍 Throwable 的相关内容。 1. 基本概念 Throwable 是 Exception 和 Error 的父接口。在 PHP 7 之前&#xff0c;异常&#xff08…...

PHP异常处理__Exception类

以下是对 PHP 中 Exception 类的详细解释&#xff1a; 一、Exception 类概述 Exception 是 PHP 中所有异常类的基类。它提供了一个通用的异常处理机制&#xff0c;用于处理程序执行过程中可能出现的错误情况。当程序中出现异常时&#xff0c;可以创建 Exception 的实例并将其…...

C++中动态多态类别浅析

非抽象类继承和虚函数 #include <iostream> using namespace std;class Base { public:virtual void func() { // 虚函数&#xff0c;支持动态绑定cout << "Base::func()" << endl;} };class Derived : public Base { public:void func() overrid…...

游戏引擎学习第234天:实现基数排序

回顾并为今天的内容设定背景 我们今天继续进行排序的相关&#xff0c;虽然基本已经完成了&#xff0c;但还是想收尾一下&#xff0c;让整个流程更完整。其实这次排序只是个借口&#xff0c;主要是想顺便聊一聊一些计算机科学的知识点&#xff0c;这些内容在我们项目中平时不会…...

系分架构论文《论高并发场景的架构设计和开发方法》

系统分析师论文范文系列 【摘要】 2022年8月&#xff0c;我司承接了某知名电商平台“秒杀系统架构优化”项目&#xff0c;我作为系统分析师主导了整体架构设计与技术选型工作。该平台在促销活动中面临瞬时流量超过50万QPS的挑战&#xff0c;原有架构存在数据库崩溃、服务响应延…...

最新得物小程序sign签名加密,请求参数解密,响应数据解密逆向分析

点击精选&#xff0c;出现https://app.dewu.com/api/v1/h5/index/fire/index 这个请求 直接搜索sign的话不容易定位 直接搜newAdvForH5就一个&#xff0c;进去再搜sign&#xff0c;打上断点 可以看到t.params就是没有sign的请求参数&#xff0c; 经过Object(a.default)该函数…...

jangow靶机笔记(Vulnhub)

环境准备&#xff1a; 靶机下载地址&#xff1a; https://download.vulnhub.com/jangow/jangow-01-1.0.1.ova kali地址&#xff1a;192.168.144.128 靶机&#xff08;jangow&#xff09;地址&#xff1a;192.168.144.180 一.信息收集 1.主机探测 使用arp-scan进行主机探…...

Spring Boot + Caffeine:打造高性能缓存解决方案

1. 引言 1.1 缓存的重要性 缓存是提升系统性能的关键技术之一,通过将频繁访问的数据存储在内存中,减少对数据库或其他外部系统的访问次数,从而降低延迟并提高吞吐量。 缓存的基本概念:缓存是一种临时存储机制,用于快速访问常用数据。缓存在提升系统性能中的作用:减少数…...

C++入门小馆: 深入string类

嘿&#xff0c;各位技术潮人&#xff01;好久不见甚是想念。生活就像一场奇妙冒险&#xff0c;而编程就是那把超酷的万能钥匙。此刻&#xff0c;阳光洒在键盘上&#xff0c;灵感在指尖跳跃&#xff0c;让我们抛开一切束缚&#xff0c;给平淡日子加点料&#xff0c;注入满满的pa…...

命令行基础

学习目标 掌握VRP命令行的基础知识 利用VRP命令行进行基本的配置 VRP命令行的基础知识 一、VRP 命令行基本架构 1. 用户视图&#xff08;User View&#xff09; 进入方式&#xff1a;设备启动后默认进入&#xff0c;提示符为 &#xff1c;HUAWEI&#xff1e;。功能&#…...

10-DevOps-Jenkins参数化构建实现多版本发布

在之前的Jenkins配置中&#xff0c;固定写死了程序的版本号&#xff0c;实际情况是随着版本的不断迭代&#xff0c;版本号也是不断变化的&#xff0c;版本号由代码仓库&#xff08;GitLab&#xff09;设置。 当前Jenkins配置是固定写的1.0&#xff0c;本节我们要把它改成动态的…...

C++游戏服务器开发之⑦redis的使用

目录 1.当前进度 2.守护进程 3.进程监控 4.玩家姓名添加文件 5.文件删除玩家姓名 6.redis安装 7.redis存取命令 8.redis链表存取 9.redis程序结构 10.hiredisAPI使用 11.基于redis查找玩家姓名 12.MAKEFILE编写 13.游戏业务实现总结 1.当前进度 2.守护进程 3.进程监…...

二进制裁剪命令mips-linux-gnu-strip 命令的使用

-s 或者--strip-all:移除所有符号和调试信息 -g 或者--strip-debug:仅移除调试信息 -d 或者--strip-unneeded:移除不需要的符号 默认不传任何参数 也是移除所有符号和调试 应用:把文件系统所有二进制镜像使用一遍,缩小文件系统大小 79K Apr 19 15:47 fat.ko //使用前 mips-l…...

【Bluedroid】蓝牙存储模块配置管理:启动、读写、加密与保存流程解析

本文围绕蓝牙存储模块展开&#xff0c;主要解析了蓝牙存储模块&#xff08;StorageModule&#xff09;的初始化流程&#xff0c;重点围绕配置文件校验、读取、设备类型修复及加密处理展开。通过工厂重置检测、校验和验证、多源配置加载、设备类型推断修正等步骤&#xff0c;确保…...

SpringBoot启动后初始化的几种方式

目录 一、静态代码块 二、构造方法 三、PostConstruct 四、InitializingBean 接口 五、 Bean 注解中的 initMethod 六、 CommandLineRunner 接口 七、ApplicationRunner 接口 八、EventListener事件 九、SmartInitializingSingleton接口 十、ApplicationListener接口…...

asp.net core webapi+efcore

简洁的restfull风格 目前c#提供了多种风格的web编程&#xff0c;因为微软有自己的前端&#xff0c;所以集成了很多内容&#xff0c;不过基于现在编程前后端分离的模式&#xff0c;webapi是合适的。 webapi 目前网络上有很多介绍&#xff0c;不反复说这个了。在建立控制器时&…...

java怎么完善注册,如果邮箱中途更换,能否判断

解析在下面 附赠代码 private static class CodeInfo {String code;long timestamp;CodeInfo(String code, long timestamp) {this.code code;this.timestamp timestamp;}}// 存储验证码&#xff08;邮箱 -> 验证码信息&#xff09;(保证线程安全) 以免中途更改邮箱pri…...

实战设计模式之备忘录模式

概述 与解释器模式、迭代器模式一样&#xff0c;备忘录模式也是一种行为设计模式。备忘录模式允许我们保存一个对象的状态&#xff0c;并在稍后恢复到这个状态。该模式非常适合于需要回滚、撤销或历史记录等功能的应用场景。通过使用备忘录模式&#xff0c;开发者可以轻松添加诸…...

数据库表设计

一对一关系 共享主键 两个表的主键是相同的 唯一外键 从表中记录主表的id 一对多关系 从表&#xff08;多的表&#xff09;存储主表的id 多对多关系 设计一个中间表&#xff08;关联表&#xff09;&#xff0c;它有两列分别记录两个主表&#xff08;A 和 B&#xff09;…...

Linux 桌面环境 LXQt 2.2 发布

Linux 桌面环境 LXQt 2.2 于 2025 年 4 月 17 日正式发布。这是该轻量级开源 Qt 桌面环境的最新稳定版本&#xff0c;带来了诸多改进&#xff0c;特别是在 Wayland 支持方面。以下是一些主要的更新内容&#xff1a; Wayland 支持增强&#xff1a; 提升了多屏支持&#xff0c;使…...

多人五子棋联机对战平台 测试报告

目录 项目介绍 测试用例设计 部分功能测试示例 自动化测试 测试范围 排除范围 自动化测试目录​编辑 执行全部自动化测试用例 性能说明 总结 性能测试 结果分析 测试总结 项目介绍 该项目基于WebSocket实现实时通信&#xff0c;采用SSM框架构建在线五子棋多人联机…...

探索 .bat 文件:自动化任务的利器

在现代计算机操作中&#xff0c;批处理文件&#xff08;.bat 文件&#xff09;是一种简单而强大的工具&#xff0c;它可以帮助我们自动化重复性任务&#xff0c;工作效率提高。尽管随着编程语言和脚本工具的发展&#xff0c;.bat 文件的使用频率有所下降&#xff0c;但它依然是…...

240419 leetcode exercises

240419 leetcode exercises jarringslee 文章目录 240419 leetcode exercises[19. 删除链表的倒数第 N 个结点](https://leetcode.cn/problems/remove-nth-node-from-end-of-list/)&#x1f501; 经典方法&#xff1a;两次遍历暴力求解&#x1f501; 双指针法 &#xff1a;快慢…...

开源Midjourney替代方案:企业级AI绘画+PPT生成系统+AI源码

「AI取代设计师&#xff1f;」开源Midjourney替代方案&#xff1a;企业级AI绘画PPT生成系统 ——零代码私有化部署&#xff0c;5倍速出图100%版权合规 设计师行业的危机与机遇 1. 传统设计流程的致命短板 痛点 人工设计 AI系统 单张海报耗时 3小时&#xff08;含反复修改…...

学习笔记十七——Rust 支持面向对象编程吗?

&#x1f9e0; Rust 支持面向对象编程吗&#xff1f; Rust 是一门多范式语言&#xff0c;主要以 安全、并发、函数式、系统级编程为核心目标&#xff0c;但它同时也支持面向对象的一些关键特性&#xff0c;比如&#xff1a; 特性传统 OOP&#xff08;如 Java/C&#xff09;Ru…...

图灵奖得主LeCun:DeepSeek开源在产品层是一种竞争,但在基础方法层更像是一种合作;新一代AI将情感化

图片来源&#xff1a;This is World 来源 | Z Potential Z Highlights&#xff1a; 新型的AI系统是以深度学习为基础&#xff0c;能够理解物理世界并且拥有记忆、推理和规划能力的。一旦成功构建这样的系统&#xff0c;它们可能会有类似情感的反应&#xff0c;但这些情感是基…...

Flink框架十大应用场景

Flink框架适合应用的场景 1. 流式数据处理 Flink框架最常用的应用场景是流式数据处理。流式数据处理是指对实时数据进行处理,以便及时地做出决策。例如,一个电商网站需要对用户的行为进行实时分析,以便根据用户的兴趣和行为推荐商品。Flink框架可以帮助电商网站实时地处理数…...

C++镌刻数据密码的树之铭文:二叉搜索树

文章目录 1.二叉搜索树的概念2.二叉搜索树的实现2.1 二叉搜索树的结构2.2 二叉搜索树的节点寻找2.2.1 非递归2.2.2 递归 2.3 二叉搜索树的插入2.3.1 非递归2.3.2 递归 2.4 二叉搜索树的删除2.4.1 非递归2.4.2 递归 2.5 二叉搜索树的拷贝 3.二叉树的应用希望读者们多多三连支持小…...

CAN与CANFD协议说明

在 CAN&#xff08;Controller Area Network&#xff0c;控制器局域网&#xff09;协议里&#xff0c;仲裁域波特率和数据域比特率有着不同的含义和作用&#xff0c;下面为你详细介绍并举例说明。 概念解释 仲裁域波特率 含义&#xff1a;仲裁域是 CAN 数据帧中的一部分&…...

【C++ Qt】信号和槽(内配思维导图 图文并茂 通俗易懂)

每日激励&#xff1a;“不设限和自我肯定的心态&#xff1a;I can do all things。 — Stephen Curry” 绪论​&#xff1a; 本章是Qt中的第三章&#xff0c;也是我们理解Qt中必备的点 信号槽&#xff0c;它本质由信号和槽两个来实现&#xff0c;其中将细致的讲述如何自定义信号…...

【实战】在 Linux 上使用 Nginx 部署 Python Flask 应用

在 Linux 上使用 Nginx 部署 Python Flask 应用 步骤一&#xff1a;准备 Flask 应用 创建 Flask 应用 确保你有一个可以运行的 Flask 应用。例如&#xff0c;创建一个简单的 app.py 文件&#xff1a; from flask import Flask app Flask(__name__)app.route(/) def hello_wor…...

java ai 图像处理

Java AI 图像处理 图像处理是人工智能&#xff08;AI&#xff09;领域中非常重要的一个应用方向。通过使用Java编程语言和相应的库&#xff0c;我们可以实现各种图像处理任务&#xff0c;如图像识别、图像分类、图像分割等。本文将介绍一些常见的图像处理算法&#xff0c;并通过…...

【绘制图像轮廓】图像处理(OpenCV) -part7

15 绘制图像轮廓 15.1 什么是轮廓 轮廓是一系列相连的点组成的曲线&#xff0c;代表了物体的基本外形。相对于边缘&#xff0c;轮廓是连续的&#xff0c;边缘不一定连续&#xff0c;如下图所示。轮廓是一个闭合的、封闭的形状。 轮廓的作用&#xff1a; 形状分析 目标识别 …...

Mesh模型孔洞修补算法总汇

关于Mesh 孔洞修补算法&#xff08;Hole Filling in Meshes&#xff09;&#xff0c;这是计算几何和图形学中的一个重要话题&#xff0c;常用于重建、3D 扫描、建模等领域。下面我会系统总结主流和经典的孔洞修补方法&#xff0c;并按技术路线分类说明每种的原理、优缺点&#…...

ARINC818协议(六)

上图中&#xff0c;红色虚线上面为我们常用的simple mode简单模式&#xff0c;下面和上面的结合在一起&#xff0c;就形成了extended mode扩展模式。 ARINC818协议 container header容器头 ancillary data辅助数据 视频流 ADVB帧映射 FHCP传输协议 R_CTRL:路由控制routing ctr…...

RTMP握手流程

RTMP&#xff08;Real-Time Messaging Protocol&#xff09; 不支持除H.264/AAC之外的标准。 使用TCP,当到达网络拥塞、宽带上限时&#xff0c;传输质量受到影响。 URL格式&#xff1a; rtmp://host:port/app&#xff08;名称&#xff09;/stream&#xff08;流ID&#xff…...