【C数据结构】 TAILQ双向有尾链表的详解
TAILQ双向有尾链表的详解
- 常见的链表结构
- 1.SLIST
- 2.STAILQ
- 3.LIST
- 4.TAILQ
- 5.CIRCLEQ
- 一、TAILQ链表简介
- 二、TAILQ的定义和声明
- 三、TAILQ队列的函数
- 1.链表头的初始化
- 2.获取第一个节点地址
- 3.获取最后一个节点地址
- 4.链表是否为空
- 5.下一个节点地址
- 6.上一个节点地址
- 7.插入头节点
- 8.插入尾节点
- 9.高效获取链表的最后一个节点地址
- 10.移除节点
- 11.遍历链表所有节点(只能读)
- 12.安全的遍历链表节点
- 其他的函数
- 四、TAILQ的使用
TAILQ是 Tail Queue 的缩写,意为双向有尾链表,是FreeBSD中的一个队列宏,属于 sys/queue.h 头文件的一部分,用于实现双向队列数据结构。
常见的链表结构
在 sys/queue.h 文件定义了5种链表结构,他们的结构分别如下:
1.SLIST
singly-linked list,意为单向无尾链表;
注意:“单向无尾链表”中的无尾通常指的是链表的实现中没有维护指向尾节点(最后一个节点)的指针

2.STAILQ
Singly-linked Tail queue,单向有尾链表;

3.LIST
双向无尾链表

4.TAILQ
Tail queue, 双向有尾链表

5.CIRCLEQ
双向循环链表

TAILQ 的设计类似于维护头尾指针的优化双向链表,所有头尾操作均为 O(1),通过二级指针(tqe_prev
)进一步优化了删除和插入逻辑,避免了显式依赖前驱节点的位置。
一、TAILQ链表简介
TAILQ的链表结构图如下:
TAILQ 链表介绍:
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
由于具有在头部或尾部插入、删除节点的操作时间复杂度均为 O(1) ,适合于高频队列操作(如消息队列、任务调度等)
二、TAILQ的定义和声明
- 节点中需要包含 TAILQ_ENTRY(type) 这一个字段。
#define TAILQ_ENTRY(type) \
struct { \struct type *tqe_next; /* next element */ \struct type **tqe_prev; /* address of previous next element */
}
tqe_next 指向的是下一个节点的地址,而 tqe_prev 为一个二级指针,指向的是前一个节点的 tqe_next (指针)的地址,解引用之后, (*tqe_next )表示的就是当前节点的地址。
- 保存首尾信息的链表头声明
TAILQ队列中需要记录头节点和尾节点的地址,因此定义了HEAD结构:
#define TAILQ_HEAD(name, type) \
struct name { \struct type *tqh_first; /* first element */ \ /* 指向第一个节点 */struct type **tqh_last; /* addr of last next element */ /* 指向最后一个节点的 tqe_next 字段地址 */
}
同样的,tqh_last 为二级指针,指向的是最后一个节点的指针的地址,同上,解引用之后,获取到尾节点地址(*tqh_last)。
三、TAILQ队列的函数
1.链表头的初始化
#define TAILQ_INIT(head) do { \(head)->tqh_first = NULL; \(head)->tqh_last = &(head)->tqh_first; \
} while (0)
head
表示链表头地址,可以看到,初始化时队列为空时,tqh_last 指针指向的是 tqh_first 的地址。
2.获取第一个节点地址
#define TAILQ_FIRST(head) ((head)->tqh_first) // 第一个元素地址
3.获取最后一个节点地址
#define TAILQ_LAST(head, headname) \(*(((struct headname *)((head)->tqh_last))->tqh_last)) // 最后一个元素地址
这个宏定义我们可以看到有两个**tqh_last
** ,以下逐步解释这个宏的实现:
-
tqh_last
的指向:链表头的tqh_last
字段指向最后一个节点的tqe_next
字段的地址,(head)->tqh_last)
表示的是最后一个节点的tqe_next字段的地址,当然最后一个节点tqe_next指针指向的为NULL。 -
强制类型转换: 由于 TAILQ_HEAD的结构体和TAILQ_ENTRY结构体分布一样,这里可以将
(head)->tqh_last)
地址强制转换为 TAILQ_HEAD 的类型,因此((struct headname *)((head)->tqh_last))
就等同于((struct entries*)(&tqe_next)
;因此再获取tqh_last
地址便是获取节点中tqe_prev
的地址。
最终的结果就是:当强制转换后,(((struct headname *)((head)->tqh_last))->tqh_last)
就是 &node->entries.tqe_prev
。
- 解引用:最后解引用就能得到最后一个节点的地址。
下面该示意图可以参考:
这里有点啰嗦,总之就是:将节点的 tqe_next
字段地址假装成链表头的地址,以便后续访问继续使用链表头结构体的字段。
4.链表是否为空
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
5.下一个节点地址
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
6.上一个节点地址
#define TAILQ_PREV(elm, headname, field) \(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
这里和前面获取最后一个节点地址的原理一样。
7.插入头节点
#define TAILQ_INSERT_HEAD(head, elm, field) do { \if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ // 如果head不为空,则插入到首节点TAILQ_FIRST((head))->field.tqe_prev = \&TAILQ_NEXT((elm), field); \else \(head)->tqh_last = &TAILQ_NEXT((elm), field); \ // 如果head为空,则elm指向的是第一个节点TAILQ_FIRST((head)) = (elm); \(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
} while (0)
如果队列不为空,则操作流程示意如下:

8.插入尾节点
#define TAILQ_INSERT_TAIL(head, elm, field) do { \QMD_TAILQ_CHECK_TAIL(head, field); \TAILQ_NEXT((elm), field) = NULL; \ /* 新节点的next指针为NULL; */(elm)->field.tqe_prev = (head)->tqh_last;\/*新节点的tqe_prev指针赋值为(head)->tqh_last;*/*(head)->tqh_last = (elm);\ /* *((head)->tqh_last) = (elm); 把尾巴节点的tqe_next指向新节点elm*/(head)->tqh_last = &TAILQ_NEXT((elm), field);\ /* 更新链表头的尾指针地址 */
} while (0)
9.高效获取链表的最后一个节点地址
#define TAILQ_LAST_FAST(head, type, field) \(TAILQ_EMPTY(head) ? NULL : __containerof((head)->tqh_last, QUEUE_TYPEOF(type), field.tqe_next))
展开得到下面的宏定义:
#define TAILQ_LAST_FAST(head, type, field) \(TAILQ_EMPTY(head) ? NULL : \((type *)((char *)((head)->tqh_last) - offsetof(type, field.tqe_next)))
其中,type为节点的类型名,field为节点中tqe_next所属的字段名。
操作流程:
(head)->tqh_last
:
链表头的tqh_last
字段指向最后一个节点的tqe_next
字段地址(例如&nodeN->field.tqe_next
)。offsetof(type, field.tqe_next)
:
计算tqe_next
字段在节点结构体type
中的偏移量(字节数)。- 指针运算:
将(head)->tqh_last
(类型为char*
)减去tqe_next
字段的偏移量,得到节点自身的起始地址。 - 类型转换:
将结果转换为type*
(节点类型指针),即最后一个节点的地址。
与 TAILQ_LAST
的对比
宏 | 实现方式 | 性能 | 适用场景 |
---|---|---|---|
TAILQ_LAST | 通过链表头的 tqh_last 字段解引用两次,依赖结构体内存布局的间接操作 | 较慢 | 标准实现,兼容性强 |
TAILQ_LAST_FAST | 直接通过指针偏移计算节点地址,减少解引用层级 | 更快 | 需要高频访问尾节点的优化场景 |
性能优势:
- 减少解引用次数:
TAILQ_LAST
需要两次解引用(head->tqh_last
和tqe_prev
),而TAILQ_LAST_FAST
直接通过指针运算定位节点地址。 - 避免类型转换风险:直接计算偏移量,无需依赖链表头和节点字段的内存布局一致性。
10.移除节点
#define TAILQ_REMOVE(head, elm, field) do { \if ((TAILQ_NEXT((elm), field)) != NULL) \TAILQ_NEXT((elm), field)->field.tqe_prev = \(elm)->field.tqe_prev; \else { \(head)->tqh_last = (elm)->field.tqe_prev; \} \*((elm)->field.tqe_prev) = TAILQ_NEXT((elm), field); \
} while (0)
从这个移除节点的操作我们可以看到,这里不需要对链表进行遍历,时间复杂度为O(1);
当next节点不为空时,移除一个节点流程示意图如下:

如果next为空时:

11.遍历链表所有节点(只能读)
#define TAILQ_FOREACH(var, head, field) /* 遍历链表,var接收节点地址 */for ((var) = TAILQ_FIRST((head)); \(var); \(var) = TAILQ_NEXT((var), field))
var
: 当前节点的指针head
: 链表头指针field
: 节点中链接字段的名称(如entries
)
如果在遍历过程中删除或修改当前节点(var
),后续的 TAILQ_NEXT
操作会访问无效指针,导致未定义行为(如崩溃或数据损坏)。
12.安全的遍历链表节点
为了防止出现遍历过程中删除或修改节点(var)引发的问题,需要使用一个 tvar 变量保存下一个节点的地址。
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \for ((var) = TAILQ_FIRST((head)); \(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \(var) = (tvar))
var
:当前节点的指针head
:链表头指针field
:节点中链接字段的名称tvar
:临时变量,用于保存下一个节点的指针
安全性:在每次迭代前,tvar
会提前保存下一个节点的指针。即使当前节点(var
)被删除,tvar
仍然有效,可以继续遍历。
(var) && ((tvar) = TAILQ_NEXT((var), field), 1);
这一个语句中,继续遍历的条件时,var不为NULL;每次判断时,就把下一个节点的地址保存到tvar
中。
((tvar) = TAILQ_NEXT(var, field), 1)
这段代码包含两个关键部分:
1.先执行(tvar) = TAILQ_NEXT(var, field)
语句,tvar
变量保存下一个节点地址。
2.逗号操作符 ,
:在 C 语言中,逗号操作符会按顺序执行其左右两边的表达式,并返回最后一个表达式的结果。
- 例如:
(b = 2, 3)
的最终b的值为3
。
强制返回值 1
:无论左侧表达式的结果如何,逗号操作符右侧的 1
会作为整个表达式的最终值。((tvar) = TAILQ_NEXT(var, field), 1)
整个表达式,值恒为1。
其他的函数
- 倒序遍历所有节点
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \for ((var) = TAILQ_LAST((head), headname); \(var); \(var) = TAILQ_PREV((var), headname, field))
- 安全的倒序遍历所有节点
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \for ((var) = TAILQ_LAST((head), headname); \(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \(var) = (tvar))
- 在链表的某个已知节点(
listelm
)之后插入新节点(elm
)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field)
- 在链表的某个已知节点(
listelm
)之前插入新节点(elm
)
#define TAILQ_INSERT_BEFORE(listelm, elm, field)
四、TAILQ的使用
测试例程:
#include <stdio.h>
#include <stdlib.h>
#include <sys/queue.h>/* 定义tailq 节点结构体 */
struct tailq_node{int val;TAILQ_ENTRY(tailq_node) entries;
};/* 链表头声明 */
TAILQ_HEAD(tailq_head, tailq_node);int main(void) {printf("tailq example\n");struct tailq_head head;/* 队列头的初始化 */TAILQ_INIT(&head);/* 插入节点 node 1 (val = 3)*/struct tailq_node* node1 = malloc(sizeof(struct tailq_node));node1->val = 3;TAILQ_INSERT_HEAD(&head, node1, entries);/* 插入节点 node 2 (val = 9) */struct tailq_node* node2 = malloc(sizeof(struct tailq_node));node2->val = 9;TAILQ_INSERT_TAIL(&head, node2, entries);/* 插入节点 node 3 (val = 17) */struct tailq_node* node3 = malloc(sizeof(struct tailq_node));node3->val = 98;TAILQ_INSERT_TAIL(&head, node3, entries);/* 打印尾结点数值 */struct tailq_node* tail_node = TAILQ_LAST(&head, tailq_head);printf("tail_first->val = %d \n",tail_node->val);/* 只读遍历所有节点 */struct tailq_node *temp = NULL;printf("travel list:");TAILQ_FOREACH(temp, &head, entries){printf("%d, ",temp->val);}printf("\n");printf("remove node 1\n");TAILQ_REMOVE(&head, node1, entries);printf("travel list:");TAILQ_FOREACH(temp, &head, entries){printf("%d, ",temp->val);}return 0;
}
输出:
tailq example
tail_first->val = 98
travel list:3, 9, 98,
remove node 1
travel list:9, 98,
以上就是TAILQ的双向有尾巴链表的使用。
相关文章:
【C数据结构】 TAILQ双向有尾链表的详解
TAILQ双向有尾链表的详解 常见的链表结构1.SLIST2.STAILQ3.LIST4.TAILQ5.CIRCLEQ 一、TAILQ链表简介二、TAILQ的定义和声明三、TAILQ队列的函数1.链表头的初始化2.获取第一个节点地址3.获取最后一个节点地址4.链表是否为空5.下一个节点地址6.上一个节点地址7.插入头节点8.插入尾…...
redisson的unlock方法
//分布式方式,分布式锁,采用redisson锁 RLock lock redissonClient.getLock(userId.toString());//lock方法会无限重试。getLock底层是hash,大key是userid,小key是线程,value是重入次数 try {//boolean b lock.tryLo…...
ffmpeg 切割视频失败 ffmpeg 命令参数 -vbsf 在新版本中已经被弃用,需要使用 -bsf:v 替代
ffmpeg 切割视频失败 ffmpeg 命令参数 -vbsf 在新版本中已经被弃用,需要使用 -bsf:v 替代 从日志中可以看到问题出在第一个 ffmpeg 命令执行时: Unrecognized option vbsf.Error splitting the argument list: Option not found这是因为 ffmpeg 命令参…...
设计模式——抽象工厂模式总结
理解了前面的工厂模式后,再理解抽象工厂模式就很容易了。 工厂模式:https://blog.csdn.net/inside802/article/details/147170118?spm1011.2415.3001.10575&sharefrommp_manage_link 抽象工厂模式就是工厂模式的更加抽象化,父类不仅不承…...
JavaScript 定时器
在 JavaScript 中,定时器是实现代码在特定时间间隔执行或延迟执行的重要工具。下面我们将深入探讨定时器的相关知识。 定时器基础 setTimeout() setTimeout() 函数用于在指定的延迟时间后执行一次回调函数。它接受两个参数,第一个参数是要执行的回调函…...
企业经营决策风险
在企业的经营过程中,领导者每天都在面对大量的决策——该扩大生产还是收缩业务?该增设销售渠道还是提升产品质量?但你知道吗,企业最大的成本,不是生产成本,也不是人工成本,而是决策错误的成本&a…...
【云安全】云原生-centos7搭建/安装/部署k8s1.23.6单节点
一、节点基本配置 1、准备操作系统 2、 修改主机名 hostnamectl set-hostname master-1 hostnamectl set-hostname node1 hostnamectl set-hostname node2#验证hostnamectl status 3、修改/etc/hosts cat <<EOF >>/etc/hosts 192.168.255.137 master-1 192.168…...
【已更新完毕】2025泰迪杯数据挖掘竞赛B题数学建模思路代码文章教学:基于穿戴装备的身体活动监测
基于穿戴装备的身体活动监测 摘要 本研究基于加速度计采集的活动数据,旨在分析和统计100名志愿者在不同身体活动类别下的时长分布。通过对加速度数据的处理,活动被划分为睡眠、静态活动、低强度、中等强度和高强度五类,进而计算每个志愿者在…...
力扣每日打卡 1922. 统计好数字的数目 (中等)
力扣 1922. 统计好数字的数目 中等 前言一、题目内容二、解题方法1. 暴力解法(会超时,此法不通)2. 快速幂运算3. 组合计数的思维逻辑分析组合计数的推导例子分析思维小结论 4.官方题解4.1 方法一:快速幂 三、快速幂运算快速幂运算…...
宝塔Linux面板 - 添加站点建站时没有域名实现 IP 地址访问测试(宝塔面板建站 IP 访问)
前言 使用面板添加站点时,必须要填写一个域名用来指向程序,没有域名怎么办? 答案:域名直接写 【服务器 IP 地址】 操作步骤 如果还没有添加站点,则直接在创建站点的时候,域名那填写服务器地址即可&#…...
【GitHub探索】mcp-go,MCP协议的Golang-SDK
近期大模型Agent应用开发方面,MCP的概念比较流行,基于MCP的ToolServer能力开发也逐渐成为主流趋势。由于笔者工作原因,主力是Go语言,为了调研大模型应用开发,也接触到了mcp-go这套MCP的SDK实现。 对于企业内部而言&am…...
手撕TCP内网穿透及配置树莓派
注意: 本文内容于 2025-04-13 15:09:48 创建,可能不会在此平台上进行更新。如果您希望查看最新版本或更多相关内容,请访问原文地址:手撕TCP内网穿透及配置树莓派。感谢您的关注与支持! 之前入手了树莓派5,…...
人形机器人运动与操作: 控制、规划和学习方面的当前进展与挑战
前言 图 1:执行运动和操作任务的人形机器人:(a)HRP-4 在适应地形的同时擦拭木板[1];(b-g)Digit、Hector[2]、Atlas、H1、Justin[3]和 Apollo 取放物体;(h)iCu…...
C++ 重构muduo网络库
本项目参考的陈硕老师的思想 1. 基础概念 进程里有 Reactor、Acceptor、Handler 这三个对象 Reactor 对象的作用是监听和分发事件;Acceptor 对象的作用是获取连接;Handler 对象的作用是处理业务; 先说说 阻塞I/O,非阻塞I/O&…...
【计算机网络实践】(十二)大学校园网综合项目设计
本系列包含: (一)以太网帧分析与网际互联协议报文结构分析 (二)地址解析协议分析与传输控制协议特性分析 (三)交换机的基本操作、配置、 虚拟局域网配置和应用 (四)交…...
通过api程序的方式编辑ps的三种方式
目前只使用了第一种 ps-python-api去操作 还没有尝试其他两种方式对于第一种方式必须要开启ps程序,程序调用修改新增图层和文档时会同步到ps页面,可以直观看到修改结果...
论文阅读笔记——Reactive Diffusion Policy
RDP 论文 通过 AR 提供实时触觉/力反馈;慢速扩散策略,用于预测低频潜在空间中的高层动作分块;快速非对称分词器实现闭环反馈控制。 ACT、 π 0 \pi_0 π0 采取了动作分块,在动作分块执行期间处于开环状态,无法及时响…...
MySQL表的增删改查进阶版
Mysql 1、数据库的约束1.1约束类型1.2 NULL约束1.3 UNIQUE:唯一约束1.4 DEFAULT:默认值约束1.5 PRIMARY KEY:主键约束(重点)1.6 FOREIGN KEY:外键约束(重点) 2.表的设计2.1一对一2.2…...
【C#】Socket通信的使用
在C#中,Socket通信是一种用于实现网络通信的底层技术。通过Socket,程序可以在网络上与其他设备进行数据交换。以下是如何使用C#中的System.Net.Sockets命名空间来实现Socket通信的详细步骤。 1. Socket通信的基本概念 Socket: 一个Socket是网络通信的端…...
linux以C方式和内核交互监听键盘[香橙派搞机日记]
最近在深入研究我的香橙派,不可避免的遇到了怎么认识和使用Linux内核的问题。 我给自己留了一个简单的任务:使用原生C来监听内核,实现读取键盘的消息。 CSDN上也有其他文章来解决这个问题,不过要么是技术不达标(直接和…...
【C++初学】课后作业汇总复习(七) 指针-深浅copy
1、 HugeInt类:构造、、cout Description: 32位整数的计算机可以表示整数的范围近似为-20亿到+20亿。在这个范围内操作一般不会出现问题,但是有的应用程序可能需要使用超出上述范围的整数。C可以满足这个需求,创建功能强大的新的…...
【iOS】UIPageViewController学习
UIPageViewController学习 前言创建一个UIPageViewController最简单的使用 UIPageViewController的方法说明:效果展示 UIPageViewController的协议方法 前言 笔者最近在写项目时想实现一个翻书效果,上网学习到了UIPageViewController今天写本篇博客总结…...
GDB 调试命令详解:高效掌握常用调试技巧
🐞 GDB 调试命令详解:高效掌握常用调试技巧 GNU Debugger(GDB)是 Linux 下最强大的 C/C 调试工具。本文将系统梳理 GDB 的常用命令,覆盖运行控制、断点管理、变量查看、线程与进程调试等核心功能,助你快速掌…...
实验二 用递归下降法分析表达式实验
【实验目的】 1.掌握用递归下降分析法进行语法分析的方法。加深对自顶向下语法分析原理的理解。 2.掌握设计、编制并调试自顶向下语法分析程序的思想和方法。 3.本实验是高级语言程序设计、数据结构和编译原理中词法分析、自顶向下语法分析原理等知 识的综合。由于语法分析…...
【随身wifi】青龙面板保姆级教程
0.操作前必看 本教程基于Debian系统,从Docker环境。面板安装,到最后拉取脚本的使用。 可以拉库跑狗东京豆,elm红包等等,也可以跑写自己写的脚本,自行探索 重要的号别搞,容易黑号,黑号自己负责…...
从一到无穷大 #45:InfluxDB MCP Server 构建:从工程实践到价值重构
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作),由 李兆龙 确认,转载请注明版权。 文章目录 工程实践遇到的问题MCP Host选择开发流程 结果展现可能性展望工作生活带来的变化 MCP…...
app逆向专题五:新快报app数据采集
app逆向专题五:新快报app数据采集 一、抓包寻找数据接口二、编写代码三、完整代码一、抓包寻找数据接口 打开charles,并在手机端打开新快报app,点击“广州”或者“经济”等选项卡,抓包,寻找数据接口,如图所示: 二、编写代码 这里介绍一种简便的代码编写方法,在数据…...
使用 lm-eval 评估模型时报错:TypeError: ‘NoneType’ object is not callable 的解决方案
问题描述 在使用 lm-evaluation-harness 进行多 GPU 模型评估时,使用如下命令: accelerate launch --multi-gpu --num_processes 2 \-m lm_eval --model hf \--model_args pretrained${local_model_path} \--tasks mmlu \--batch_size 8 \--log_sample…...
脉冲耦合神经网络(PCNN):图像处理中的强大工具
文章目录 一、PCNN 的起源与背景二、PCNN 的基本原理(一)模型结构(二)工作方式(三)动态阈值与脉冲特征三、PCNN 在图像处理中的应用(一)图像分割(二)边缘检测(三)纹理分析四、PCNN 的实现与优化环境准备PCNN 类定义图像分割示例在图像处理和计算机视觉领域,神经网…...
【Git】从零开始使用git --- git 的基本使用
哪怕是野火焚烧,哪怕是冰霜覆盖, 依然是志向不改,依然是信念不衰。 --- 《悟空传》--- 从零开始使用git 了解 Gitgit创建本地仓库初步理解git结构版本回退 了解 Git 开发场景中,文档可能会经历若干版本的迭代。假如我们不进行…...
React Hooks 的使用
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 🍚 蓝桥云课签约作者、…...
【NIO番外篇】之组件 Channel
目录 一、什么是NIO Channel?二、常见的Channel组件及其用法1. FileChannel2. SocketChannel3. ServerSocketChannel4. DatagramChannel 🌟我的其他文章也讲解的比较有趣😁,如果喜欢博主的讲解方式,可以多多支持一下&a…...
探秘 Ruby 与 JavaScript:动态语言的多面风采
1 语法特性对比:简洁与灵活 1.1 Ruby 的语法优雅 Ruby 的语法设计旨在让代码读起来像自然语言一样流畅。它拥有简洁而富有表现力的语法结构,例如代码块、符号等。 以下是一个使用 Ruby 进行数组操作的简单示例: # 定义一个数组 numbers [1…...
高频面试题(含笔试高频算法整理)基本总结回顾21
干货分享,感谢您的阅读! (暂存篇---后续会删除,完整版和持续更新见高频面试题基本总结回顾(含笔试高频算法整理)) 备注:引用请标注出处,同时存在的问题请在相关博客留言…...
深入浅出一下Python函数的核心概念与进阶应用
本篇技术博文摘要 🌟 本文系统梳理了Python函数的核心知识点,从基础概念到高级特性,构建了完整的函数编程知识体系。内容涵盖:变量作用域的局部与全局划分、函数注释的规范写法、参数传递中值与引用的区别、匿名函数的灵活应用&am…...
【漫话机器学习系列】198.异常值(Outlier)
异常值(Outlier)全面指南 —— 检测、分析与处理 作者:Chris Albon(图源) 场景:数据清洗与特征工程必备技能 一、什么是异常值(Outlier) 定义 异常值(Outlier࿰…...
React 记账本项目实战:多页面路由、Context 全局
在本文中,我们将分享一个使用 React 开发的「记账本」项目的实战经验。该项目通过 VS Code 完成,包含首页、添加记录页、编辑页等多个功能页面,采用了 React Router 实现路由导航,使用 Context API 管理全局的交易记录状态,并引入数据可视化组件呈现不同月份的支出情况。项…...
[React] 如何用 Zustand 构建一个响应式 Enum Store?附 RTKQ 实战与 TS 架构落地
[React] 如何用 Zustand 构建一个响应式 Enum Store?附 RTKQ 实战与 TS 架构落地 本文所有案例与数据为作者自行构建,所有内容均为技术抽象示例,不涉及任何实际商业项目 自从之前尝试了一下 zustand 之后,就发现 zustand 是一个轻…...
DeepSeek在职场办公中的高效指令运用与策略优化
摘要 随着人工智能技术的飞速发展,大型语言模型在各个领域的应用日益广泛。DeepSeek作为一款具有影响力的AI产品,为职场办公带来了新的变革与机遇。本文深入剖析DeepSeek在职场办公场景下的提示词指令运用,通过对提示词概念、作用、设计原则的…...
mysql事务脏读 不可重复读 幻读 事务隔离级别关系
看了很多文档,发现针对事务并发执行过程中的数据一致性问题,即脏读、不可重复读、幻读的解释一塌糊涂,这也不能说什么,因为官方SQL标准中的定义也模糊不清。 按照mysql中遵循的事务隔离级别,可以梳理一下其中的关系 隔…...
Fork/Join框架与线程池对比分析
Fork/Join框架与线程池对比分析 1. 概述 线程池(如ThreadPoolExecutor)是Java并发编程中用于管理线程生命周期的通用工具,适用于处理大量独立任务。Fork/Join框架(基于ForkJoinPool)是Java 7引入的专用框架ÿ…...
docker 安装 Gitlab
GitLab 安装 #创建容器数据卷映射目录 mkdir -p /usr/docker/gitlab/config mkdir -p /usr/docker/gitlab/logs mkdir -p /usr/docker/gitlab/data #目录授权 chmod 777 -R /usr/docker/gitlab/*#直接复制可用(记得改下宿主机ipv4,不知道怎么看,输入i…...
【贪心之摆动序列】
题目: 分析: 这里我们使用题目中给的第二个实例来进行分析 题目中要求我们序列当中有多少个摆动序列,摆动序列满足一上一下,一下一上,这样是摆动序列,并且要输出摆动序列的最长长度 通过上面的图我们可以…...
kubectl修改资源时添加注解
kubectl修改资源时添加注解 kubectl修改资源时添加注解老版本的注解(变化注解)删除Annotations查看Annotations信息 查看发布记录回滚 kubectl修改资源时添加注解 参考: 为什么我们要使用kubectl apply 修改资源时,在命令行后添加 --save-configtrue ,就会自动添加此次修改的…...
【C++初学】课后作业汇总复习(四) 复数类与运算符重载
1、复数类输出 如题,要求实现: 1、复数类含两个参数的构造函数,一个为实部,一个为虚部 2、用Show()现实复数的值。 输出 (23i) //如题,要求实现: // //1、复数类含两个参数的构造函数&…...
十四、C++速通秘籍—函数式编程
目录 上一章节: 一、引言 一、函数式编程基础 三、Lambda 表达式 作用: Lambda 表达式捕获值的方式: 注意: 四、函数对象 函数对象与普通函数对比: 五、函数适配器 1、适配普通函数 2、适配 Lambda 表达式 …...
复刻系列-星穹铁道 3.2 版本先行展示页
复刻星穹铁道 3.2 版本先行展示页 0. 视频 手搓~星穹铁道~展示页~~~ 1. 基本信息 作者: 啊是特嗷桃系列: 复刻系列官方的网站: 《崩坏:星穹铁道》3.2版本「走过安眠地的花丛」专题展示页现已上线复刻的网…...
阿里云备案有必要选择备案管家服务吗?自己ICP备案可以吗?
阿里云备案有必要选择备案管家服务吗?新手可以选择备案管家,备案管家不需要自己手动操作,可以高效顺利通过ICP备案。自己ICP备案可以吗?自己备案也可以的,也很简单,适合动手能力强的同学。 阿里云备案管家…...
SQL语言基础(二)--以postersql为例
上次教程我们讲述了数据库中的增,删,改语句,今天我们来学习最后一个–‘改’的语句。 1.select语法 数据库查询只有select一个句子,但select语法相对复杂,其功能丰富,使用方式也很灵活 SELECT [ALL|Dist…...
探索 Rust 语言:高效、安全与并发的完美融合
在当今的编程语言领域,Rust 正以其独特的魅力吸引着越来越多开发者的目光。它诞生于 Mozilla 实验室,旨在解决系统编程中长久以来存在的难题,如今已成为构建可靠、高效软件的有力工具。 1 内存安全 Rust 通过所有权(ownership&a…...