【数据结构】单向链表实现 超详细
目录
一. 单链表的实现
1.准备工作及其注意事项
1.1 先创建三个文件
1.2 注意事项:帮助高效记忆和理解
2.链表的基本功能接口
2.0 创建一个 链表
2.1 链表的打印
3.链表的创建新节点接口
4.链表的节点插入功能接口
4.1 尾插接口
4.2 头插接口
4.3 指定位置 pos 之前 插入接口
4.4 指定位置pos 之后 插入接口(推荐)
5.链表表的删除功能接口
5.1 尾删接口
5.2头删接口
5.3 删除 指定位置 pos 节点 接口
5.4 删除 指定位置 pos ==之后== 的一个 节点 接口
6.链表的 查找 接口
7.链表的 销毁 接口
二、总代码
SList.h
SList.c
test.c
前言:(受篇幅限制,为了划清知识模块,进行分章节讲解)
若想了解清楚 链表的概念和分类 :
可以点击跳转 这一章节----> :【数据结构】链表的概念 及 分类 (使用比喻解释概念)
一. 单链表的实现
1.准备工作及其注意事项
1.1 先创建三个文件

解释这三个文件的作用
1、头文件SList.h 是来声明接口函数,定义链表,将几个公共用到的库函数集合起来
2、源文件SList.c 是用来具体实现接口
3、源文件test.c 用于接口的测试工作 ,即具体的使用场景
1.2 注意事项:帮助高效记忆和理解
1. 但凡是删除,必须 断言 链表不能为 空,避免传过来的是NULL指针 assert(*pphead);
2. 传递二级指针,要断言 不能为 NULL ,指针不能为空:assert(pphead);
3. 指定位置 pos 时,要确保pos存在:assert(pos);
4. (pphead)->next; 解引用前一定要加 括号,* 号 优先级 < 箭头操作符
5. 链表的所有插入接口:链表为空: 没有节点 就直接插入
6.要找 前一个节点 prev 或 后一个节点 next 时, 一定要确保 其本身存在!!!!
7.当指定位置 pos 刚好是 头节点时 就没有 prev 了
2.链表的基本功能接口
2.0 创建一个 链表
// 创建链表节点结构体
// 和顺序表创建原理相同,可以看我的 【顺序表】章节
typedef int SLDataType;
typedef struct SListNode
{SLDataType data;struct SListNode* next;
}SLTNode;
2.1 链表的打印
// 打印函数
//注意这里的 phead 仅表示单向链表传递过来的 第一个节点地址,不是 双向链表的带头节点
void SLTPrint(SLTNode* phead)
{SLTNode* ptemp = phead;while (ptemp) // ptemp != NULL 说明为是有效的节点,则循环打印{printf("%d -> ", ptemp->data);ptemp = ptemp->next; // 更新 ptemp :保存下个节点的地址:遍历节点的关键}printf("NULL\n");
}
3.链表的创建新节点接口
// 创建新节点函数:所有的插入接口,都需要 创建新节点
SLTNode* STLCreatNode(SLDataType x)
{// 先创建 一个 新节点SLTNode* newNode = (SLTNode*)malloc(sizeof(SLTNode));newNode->data = x;newNode->next = NULL;return newNode;
}
4.链表的节点插入功能接口
4.1 尾插接口
链表的尾插法
几种情况
1、链表非空:先找到尾节点,然后直接将尾节点 的 next 指向 新节点,(形成链接), 新节点 则 指向 NULL 变成 新的尾节点
2、链表为空:将头指针phead更新 成 新插入节点的地址,然后新节点 next 指向 NULL

// 注意:plist 头指针是 时刻更新为了指向头节点,当创建新的头节点后,要更新 plist ,而若要真正改变 plist 的值,需要传地址
// 传递过来的 头指针 &plist 是 一级指针 plist 的地址,要用 二级指针 pphead 来接收 ; 而 *pphead == plist
// void SLTPushBack(SLTNode* phead, SLDataType x) // 错误写法:传值void SLTPushBack(SLTNode** pphead, SLDataType x)
{assert(pphead); // 传递过来的 一级指针的地址 二级指针 不能为 NULL// 先创建 一个 新节点SLTNode* newNode = STLCreatNode(x);// 1、链表为空if (*pphead == NULL){*pphead = newNode;return;}// 2、链表非空SLTNode* ptail = *pphead; // 因为要找 尾节点:干脆命名为 tail//本质是:找到尾节点的地址,而不是 尾节点的 nextwhile (ptail->next) {ptail = ptail->next;}ptail->next = newNode;
}
4.2 头插接口

void SLTPushFront(SLTNode** pphead, SLDataType x)
{assert(pphead); // 传递过来的 一级指针的地址 二级指针 不能为 NULL// 先创建 一个 新节点SLTNode* newNode = STLCreatNode(x);newNode->next = *pphead; // 先将原本的 头指针放在 新节点的next*pphead = newNode; // 更新 头指针
}
4.3 指定位置 pos 之前 插入接口

// 在指定位置节点pos 之前插入数据
// 思路:找三个节点 :prev newNode next
// 前 本身新节点 后 为了找到 地址 才能将三者链接起来
// 注意:要找 前一个节点 prev 时, 一定要确保 其本身存在!!!
// 当 pos 刚好是 头节点时 就没有 prev 了,特殊情况, 否则会因为找不到prev而 程序奔溃
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLDataType x)
{assert(pphead); // 指针不能为空assert(pos); // pos 不能为空assert(*pphead); // 链表不能为空: pos 是链表中 的一个有效节点,pos 不为空,链表也不能为 空// 创建新节点SLTNode* newNode = STLCreatNode(x);if (*pphead == pos){// 当 pos 刚好是 头节点时 就用 头插法:调用之前写的函数接口SLTPushFront(pphead, x);return;}SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}// 三者关联起来: prev -> newNode -> posnewNode->next = pos;prev->next = newNode;
}
4.4 指定位置pos 之后 插入接口(推荐)

// 在指定位置节点pos 之后插入数据: 注意一下错误写法: 和正确写法刚好相反
// 特点:有了 pos 就不需要 头节点了!!
void SLTInsertAfter(SLTNode*pos, SLDataType x)
{assert(pos); // pos 不能为空// 创建新节点SLTNode* newNode = STLCreatNode(x);newNode->next = pos->next;pos->next = newNode;
}
5.链表表的删除功能接口
5.1 尾删接口

// 链表的尾删法
// 思路:删除尾节点 以及 前一个节点 pre 的next 要置为 NULL
// 要注意 只有一个节点时,没有前置节点 pre!!!!
void SLTPopBack(SLTNode** pphead)
{assert(pphead); // 一级指针为plist 指向头节点,传递过来的 一级指针的地址二级指针不能为 NULLassert(*pphead); // 一级指针为plist 指向头节点, *pphead == plist 这个指针也不能为 NULL 不能为空链表// 链表非空// 链表只有一个节点时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;
}
5.2头删接口

// 链表的头删法
void SLTPopFront(SLTNode** pphead)
{assert(pphead); // 指针不能为空assert(*pphead); // 链表不能为空// 让第二个节点成为新的头节点// 将 旧的节点释放掉: 注意 要先将 第二个节点的地址 (*pphead) -> next 临时储存起来!!SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}
5.3 删除 指定位置 pos 节点 接口
// 删除pos 节点
// 删除 pos 节点后,还要 将 prev 和 next (pos的前后两个节点) 链接上!!: 比较麻烦
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead); // 指针不能为空assert(pos); // pos 不能为空assert(*pphead); // 链表不能为空: pos 是链表中 的一个有效节点,pos 不为空,链表也不能为 空//先 让 prev 链接上 next,后 free 释放掉 pos// 遇到 需要找 prev 的,一定要 检查是否存在 previf (*pphead == pos)//检查是否存在 prev{free(pos);*pphead = NULL;return;}SLTNode* prev = *pphead;while (prev->next != pos) // 找 前面节点 prev{prev = prev->next;}prev->next = pos->next; // 先赋值后 再free释放free(pos);pos = NULL;
}
5.4 删除 指定位置 pos ==之后== 的一个 节点 接口

// 删除pos 之后的节点
// 直接 删除 pos 之后的 节点 pos->next
// 先让 pos 和 pos -> next -> next 链接起来,后 删除 节点 pos -> next
// 注意:像这类找 上一个节点 prev 或 找下一个节点 next :都需要 检查 是否存在 该节点
void SLTEraseBeind(SLTNode* pos)
{assert(pos); // pos 不能为空assert(pos->next); // 这个节点必须存在,否则该函数无意义! 链表至少要有两个节点// 三者的关联关系:pos pos->next pos->next->next// 注意:下面 先 pos->next = pos->next->next, 后 free(pos->next) 是不对的 // pos->next 已经更新,不是你所认为的原来的那个 中间节点了,因此要先 临时保存起来!!!SLTNode* temp = pos->next;pos->next = pos->next->next;free(temp);temp = NULL;
}
6.链表的 查找 接口
// 链表的查找
SLTNode* SLTFind(SLTNode** pphead, SLDataType x)
{// 遍历链表assert(pphead); // 指针不能为空SLTNode* ptemp = *pphead;while (ptemp){if (ptemp->data == x) return ptemp; // 找到了就返回节点ptemp = ptemp->next;}// 没找到return NULL;
}
7.链表的 销毁 接口
// 销毁链表
// 一旦涉及到 动态内存申请,不要忘记销毁
void SListDestory(SLTNode** pphead)
{assert(pphead); // 指针不能为空assert(*pphead); // 链表不能为空,空的没必要销毁了// 顺序表是一段连续的空间,可以执行一次free全部销毁,而链表节点是独立的,需要遍历节点一个一个销毁// 不能直接 一个一个 free 下去,需要 保存 next 节点,才能找到下个节点!!!!SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}// 当所有节点销毁后,需要 将头指针 销毁*pphead = NULL;
}
二、总代码
SList.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>// 零、创建链表节点
typedef int SLDataType;
typedef struct SListNode
{SLDataType data;struct SListNode* next;
}SLTNode;// 一、打印函数: 这里用传址
void SLTPrint(SLTNode* ps);// 二、尾插法
void SLTPushBack(SLTNode** pphead, SLDataType x);// 三、头插法
void SLTPushFront(SLTNode** pphead, SLDataType x);// 四、尾删法
void SLTPopBack(SLTNode** pphead);// 五、头删法
void SLTPopFront(SLTNode** pphead);// 六、链表的查找
SLTNode* SLTFind(SLTNode** pphead, SLDataType x);// 七、指定位置 pos 之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLDataType x);// 八、指定位置 pos 之后 插入
void SLTInsertAfter(SLTNode* pos, SLDataType x);// 九、删除 指定 pos 节点
void SLTErase(SLTNode** pphead, SLTNode* pos);// 十、删除指定 pos 之后 的节点
void SLTEraseAfter(SLTNode* pos);// 十一、销毁链表
void SLTDestory(SLTNode** pphead);
SList.c
#include"SList.h"// 一、打印函数: 这里用传址
void SLTPrint(SLTNode* phead)
{SLTNode* ptemp = phead;while (ptemp) // ptemp != NULL 表示为有效节点 {printf("%d -> ", ptemp->data);ptemp = ptemp->next;}printf("NULL\n");
}// 创建节点函数
SLTNode* SLTCreatNode(SLDataType x)
{SLTNode* newNode = (SLTNode*)malloc(sizeof(SLTNode));newNode->data = x;newNode->next = NULL;return newNode;
}
// 二、尾插法
void SLTPushBack(SLTNode** pphead, SLDataType x)
{assert(pphead);// 创建新节点SLTNode* newNode = SLTCreatNode(x);// 链表为空: 没有节点 就直接插入if (*pphead == NULL){*pphead = newNode;return;}// 链表非空:找尾节点SLTNode* ptail = *pphead;while (ptail->next){ptail = ptail->next;}ptail->next = newNode;
}// 三、头插法
void SLTPushFront(SLTNode** pphead, SLDataType x)
{assert(pphead);// 创建新节点SLTNode* newNode = SLTCreatNode(x);// 先 链接 第一个节点, 没有第一个节点就是 NULL,也可以直接给 newNode->next,后更新 *ppheadnewNode->next = *pphead;*pphead = newNode;
}// 四、尾删法
void SLTPopBack(SLTNode** pphead)
{assert(pphead);assert(*pphead);// 先找尾节点 和 前置节点 prev,后删除+链接// 当只有一个 节点时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;
}// 五、头删法
void SLTPopFront(SLTNode** pphead)
{assert(pphead);assert(*pphead);// 让 *pphead 指向第二个节点,free 掉第一个SLTNode* next = (*pphead)->next; // 一定要加 括号,* 号 优先级 < 箭头操作符 free(*pphead);*pphead = next;
}// 六、链表的查找
SLTNode* SLTFind(SLTNode** pphead, SLDataType x)
{assert(pphead);// 遍历SLTNode* ptemp = *pphead;while (ptemp){if (ptemp->data == x) return ptemp;ptemp = ptemp->next;}return NULL;
}// 七、指定位置 pos 之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLDataType x)
{assert(pphead);assert(pos);assert(*pphead);// 涉及到三个节点: prev newNode pos// 创建新节点SLTNode* newNode = SLTCreatNode(x);if (*pphead == pos)// 无 prev 的情况{SLTPushFront(pphead, x);return;}SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}newNode->next = pos;prev->next = newNode;
}// 八、指定位置 pos 之后 插入(推荐!)
void SLTInsertAfter(SLTNode* pos, SLDataType x)
{assert(pos);// 创建新节点SLTNode* newNode = SLTCreatNode(x);newNode->next = pos->next;pos->next = newNode;
}// 九、删除 指定 pos 节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(pos);assert(*pphead);// 关系三个节点:prev pos next// 先找 prevSLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;
}// 十、删除指定 pos 之后 的节点
void SLTEraseAfter(SLTNode* pos)
{assert(pos);assert(pos->next); // 注意 pos->next 这个必须存在// 关系三个节点: pos pos->next pos->next->next// 另一种可能: pos pos->next NULLif (pos->next->next == NULL){free(pos->next);pos->next = NULL;return;}// 直接销毁 pos->next 会找不到 pos->next->next,先保存SLTNode* ptemp = pos->next->next;free(pos->next);pos->next = NULL;pos->next = ptemp;
}// 十一、销毁链表
void SLTDestory(SLTNode** pphead)
{assert(pphead);// 遍历销毁if (*pphead == NULL)return;SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}// 别忘了将 头指针销毁!!!*pphead = NULL;
}
test.c
#include"SList.h"void SLTest1()
{// 零、创建 链表SLTNode* plist = NULL;// 二、尾插法SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4); // 1 -> 2 -> 3 -> 4 -> NULLprintf("测试尾插:");SLTPrint(plist);// 三、头插法SLTPushFront(&plist, 5);SLTPushFront(&plist, 6); // 6 -> 5 -> 1 -> 2 -> 3 -> 4 -> NULLprintf("测试头插:");SLTPrint(plist);// 四、尾删法SLTPopBack(&plist);SLTPopBack(&plist); // 6 -> 5 -> 1 -> 2 -> NULLprintf("测试尾删:");SLTPrint(plist);// 五、头删法SLTPopFront(&plist);SLTPopFront(&plist); // 1 -> 2 -> NULLprintf("测试头删:");SLTPrint(plist);// 六、链表的查找SLTNode* FindRet = SLTFind(&plist, 2);printf("测试查找:");if (FindRet) printf("找到了\n");else printf("没找到\n");// 七、指定位置 pos 之前插入SLTNode* FindRet1 = SLTFind(&plist, 2);SLTNode* FindRet2 = SLTFind(&plist, 1);SLTInsert(&plist, FindRet1, 200);SLTInsert(&plist, FindRet2, 100);// 100 -> 1 -> 200 -> 2 -> NULLprintf("测试指定位置之前插入:");SLTPrint(plist);// 八、指定位置 pos 之后 插入(推荐!)SLTNode* FindRet3 = SLTFind(&plist, 2);SLTInsertAfter(FindRet3, 200); // 100 -> 1 -> 200 -> 2 -> 200 -> NULLprintf("测试指定位置之后插入:");SLTPrint(plist);// 九、删除 指定 pos 节点SLTNode* FindRet4 = SLTFind(&plist, 1);SLTErase(&plist, FindRet4);// 100 -> 200 -> 2 -> 200 -> NULLprintf("测试删除指定节点:"); SLTPrint(plist);// 十、删除指定 pos 之后 的节点SLTNode* FindRet5 = SLTFind(&plist, 200); // 注意如果 通过 Find 函数 找 节点,节点中有重复数据,返回 第一个遇到的SLTEraseAfter(FindRet5);// 100 -> 200 -> 200 -> NULLprintf("测试删除指定节点:"); SLTPrint(plist);//
}
int main()
{SLTest1();return 0;
}
完。
若上述文章有什么错误,欢迎各位大佬及时指出,我们共同进步!
相关文章:
【数据结构】单向链表实现 超详细
目录 一. 单链表的实现 1.准备工作及其注意事项 1.1 先创建三个文件 1.2 注意事项:帮助高效记忆和理解 2.链表的基本功能接口 2.0 创建一个 链表 2.1 链表的打印 3.链表的创建新节点接口 4.链表的节点插入功能接口 4.1 尾插接口 4.2 头插接口 4.3 指定位…...
Opencc4j 开源中文繁简体使用介绍
Opencc4j Opencc4j 支持中文繁简体转换,考虑到词组级别。 Features 特点 严格区分「一简对多繁」和「一简对多异」。 完全兼容异体字,可以实现动态替换。 严格审校一简对多繁词条,原则为「能分则不合」。 词库和函数库完全分离,…...
vue 下载二进制文件
文章目录 概要技术细节 概要 vue 下载后端返回的二进制文件流 技术细节 import axios from "axios"; const baseUrl process.env.VUE_APP_BASE_API; //downLoadPdf("/pdf/download?pdfName" res .pdf, res); export function downLoadPdf(str, fil…...
数据结构之堆排序
对于几个元素的关键字序列{K1,K2,…,Kn},当且仅当满足下列关系时称其为堆,其中 2i 和2i1应不大于n。 { K i ≤ K 2 i 1 K i ≤ K 2 i 或 { K i ≥ K 2 i 1 K i ≥ K 2 i {\huge \{}^{K_i≤K_{2i}} _{K_i≤K_{2i1}} …...
鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之ScrollBar组件
鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之ScrollBar组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、ScrollBar组件 鸿蒙(HarmonyOS)滚动条组件ScrollBar&…...
读论文:DiffBIR: Towards Blind Image Restoration with Generative Diffusion Prior
DiffBIR 发表于2023年的ICCV,是一种基于生成扩散先验的盲图像恢复模型。它通过两个阶段的处理来去除图像的退化,并细化图像的细节。DiffBIR 的优势在于提供高质量的图像恢复结果,并且具有灵活的参数设置,可以在保真度和质量之间进…...
基于微信小程序的新生报到系统的研究与实现,附源码
博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…...
分享一下 uniapp 打包安卓apk
首先需要安装 Java 环境,这里就不做解释了 第二步:打开 mac 终端 / cmd 命令行工具 使用keytool -genkey命令生成证书 keytool -genkey -alias testalias -keyalg RSA -keysize 2048 -validity 36500 -keystore test.keystore *testalias 是证书别名&am…...
DevOps落地笔记-21|业务价值:软件发布的最终目的
上一课时介绍如何度量软件的内部质量和外部质量。在外部质量中,我们提到用户满意度是衡量软件外部质量的关键因素。“敏捷宣言”的第一条原则规定:“我们最重要的目标,是通过持续不断的及早交付有价值的软件使用户满意”。从这一点也可以看出…...
【动态规划】【前缀和】【数学】2338. 统计理想数组的数目
作者推荐 【动态规划】【前缀和】【C算法】LCP 57. 打地鼠 本文涉及知识点 动态规划汇总 C算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 LeetCode:2338. 统计理想数组的数目 给你两个整数 n 和 maxValue ,用于描述一个 理想…...
【已解决】onnx转换为rknn置信度大于1,图像出现乱框问题解决
前言 环境介绍: 1.编译环境 Ubuntu 18.04.5 LTS 2.RKNN版本 py3.8-rknn2-1.4.0 3.单板 迅为itop-3568开发板 一、现象 采用yolov5训练并将pt转换为onnx,再将onnx采用py3.8-rknn2-1.4.0推理转换为rknn出现置信度大于1,并且图像乱框问题…...
多路服务器技术如何处理大量并发请求?
在当今的互联网时代,随着用户数量的爆炸性增长和业务规模的扩大,多路服务器技术已成为处理大量并发请求的关键手段。多路服务器技术是一种并行处理技术,它可以通过多个服务器同时处理来自不同用户的请求,从而显著提高系统的整体性…...
SpringBoot - 不加 @EnableCaching 标签也一样可以在 Redis 中存储缓存?
网上文章都是说需要在 Application 上加 EnableCaching 注解才能让缓存使用 Redis,但是测试发现不用 EnableCaching 也可以使用 Redis,是网上文章有问题吗? 现在 Application 上用了 EnableAsync,SpringBootApplication࿰…...
Linux------命令行参数
目录 前言 一、main函数的参数 二、命令行控制实现计算器 三、实现touch指令 前言 当我们在命令行输入 ls -al ,可以查看当前文件夹下所有文件的信息,还有其他的如rm,touch等指令,都可以帮我们完成相应的操作。 其实运行这些…...
LLM少样本示例的上下文学习在Text-to-SQL任务中的探索
导语 本文探索了如何通过各种提示设计策略,来增强大型语言模型(LLMs)在Few-shot In-context Learning中的文本到SQL转换能力。通过使用示例SQL查询的句法结构来检索演示示例,并选择同时追求多样性和相似性的示例可以提高性能&…...
双非本科准备秋招(19.2)—— 设计模式之保护式暂停
一、wait & notify wait能让线程进入waiting状态,这时候就需要比较一下和sleep的区别了。 sleep vs wait 1) sleep 是 Thread 方法,而 wait 是 Object 的方法 2) sleep 不需要强制和 synchronized 配合使用,但 wait 强制和 s…...
使用SpringMVC实现功能
目录 一、计算器 1、前端页面 2、服务器处理请求 3、效果 二、用户登陆系统 1、前端页面 (1)登陆页面 (2)欢迎页面 2、前端页面发送请求--服务器处理请求 3、效果 三、留言板 1、前端页面 2、前端页面发送请求 &…...
spring aop实现接口超时处理组件
文章目录 实现思路实现代码starter组件 实现思路 这里使用FutureTask,它通过get方法以阻塞的方式获取执行结果,并设定超时时间: public V get() throws InterruptedException, ExecutionException ;public V get(long timeout, TimeUnit un…...
c++设计模式之装饰器模式
作用 为现有类增加功能 案例说明 class Car { public:virtual void show()0; };class Bmw:public Car { public:void show(){cout<<"宝马汽车>>"<<endl;} };class Audi:public Car { public:void show(){cout<<"奥迪汽车>>&q…...
WordPress如何实现随机显示一句话经典语录?怎么添加到评论框中?
我们在一些WordPress网站的顶部或侧边栏或评论框中,经常看到会随机显示一句经典语录,他们是怎么实现的呢? 其实,boke112百科前面跟大家分享的『WordPress集成一言(Hitokoto)API经典语句功能』一文中就提供…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
