带头双向循环链表:一种高效的数据结构
- 💓 博客主页:江池俊的博客
- ⏩ 收录专栏:数据结构探索
- 👉专栏推荐:✅cpolar ✅C语言进阶之路
- 💻代码仓库:江池俊的代码仓库
- 🔥编译环境:Visual Studio 2022
- 🎉欢迎大家点赞👍评论📝收藏⭐
文章目录
- 一、带头循环双向链表的概念及结构
- 二、使用带头双向循环链表的优势及注意事项
- 三、带头双向链表的创建
- ✨3.1 准备工作✨
- ✨3.2 创建返回链表的头结点✨
- ✨3.3 双向链表销毁✨
- ✨3.4 双向链表打印✨
- ✨3.5 双向链表尾插✨
- ✨3.6 双向链表尾删✨
- ✨3.7 双向链表头插✨
- ✨3.8 双向链表头删✨
- ✨3.9 双向链表查找✨
- ✨3.10 双向链表在pos的前面进行插入✨
- ✨3.11 双向链表删除pos位置的节点✨
- 四、源代码
- 🌅4.1 List.h 文件
- 🌅4.2 List.c 文件
- 🌅4.3 Test.c 文件
- 🌅4.4 测试结果
双向循环链表是一种复杂的数据结构,它结合了双向链表和循环链表的优点。与单向链表相比,双向链表可以双向遍历,而循环链表则可以在尾部链接到头部,形成一个闭环。这种数据结构在某些应用场景下非常有用,比如事件处理、图形界面、内存管理等。
一、带头循环双向链表的概念及结构
双向循环链表是一种特殊类型的链表,它由一系列节点组成,每个节点包含一个数据域
和两个指针域
。其中一个指针指向下一个节点,另一个指针指向前一个节点。在双向循环链表中,首节点的前一个节点是尾节点,尾节点的下一个节点是首节点,形成一个闭环
。
二、使用带头双向循环链表的优势及注意事项
【优势】:
- 高效遍历:由于带头双向循环链表可以双向遍历,因此可以在O(1)时间内访问任何节点。
- 内存高效:与双向链表相比,带头双向循环链表不需要额外的内存来存储头部节点。
- 插入和删除操作高效:在带头双向循环链表中插入和删除节点时,只需调整指针即可,无需移动大量数据。
【注意事项】:
- 初始化:在创建带头双向循环链表时,需要正确初始化所有节点的指针。
- 异常处理:在进行插入、删除等操作时,需要处理指针异常情况,如空指针或无效指针。
- 内存管理:在使用带头双向循环链表时,需要正确管理内存,避免内存泄漏或野指针。
三、带头双向链表的创建
✨3.1 准备工作✨
将代码分成三个文件可以提高代码的可读性、可维护性和可重用性。具体来说,三个文件可以分别关注以下方面:
- 配置文件:用于存储应用程序的配置信息,如数据库连接信息、应用程序名称、应用程序版本等。该文件可以在应用程序的整个生命周期内进行维护和管理,并且可以轻松地与其他开发人员共享。
- 模块文件:用于存储应用程序的各个模块,如用户管理、订单管理、产品管理等。每个模块都可以单独维护和测试,并且可以在不同的应用程序中重复使用。这有助于降低代码冗余和提高代码的可重用性。
- 入口文件:用于定义应用程序的入口,如路由、请求处理等。该文件可以控制应用程序的整个流程,并且可以轻松地与其他开发人员共享。
通过将代码分成三个文件,可以更好地组织代码结构,使其更加清晰和易于维护。同时,这也使得代码更易于测试和调试,并且可以更好地支持代码重构和优化。
#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{LTDataType data; //节点存储的数据元素struct ListNode* next; //指向前驱节点struct ListNode* prev; //指向后继节点
}ListNode; //双链表结构//几大接口
// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);
✨3.2 创建返回链表的头结点✨
动态申请一个节点作为双向链表的头节点。并将头节点的
prev
指向自己,next
也指向自己,表明这是一个双向链表,且链表为空。
// 创建返回链表的头结点.
ListNode* ListCreate()
{ListNode* head = (ListNode*)malloc(sizeof(ListNode));if (head == NULL){perror("ListCreate --> malloc");return;}head->data = -1;head->prev = head;head->next = head;return head;
}
✨3.3 双向链表销毁✨
从链表的第二个节点开始,逐个释放链表中的节点,直到回到头节点并释放头节点的内存空间。这样做可以确保链表中的所有节点都被正确释放,防止内存泄漏。
// 双向链表销毁
void ListDestory(ListNode* pHead)
{assert(pHead);ListNode* cur = pHead->next;while (cur != pHead){ListNode* next = cur->next;free(cur);cur = next; }free(pHead);printf("双链表销毁成功!\n");
}
✨3.4 双向链表打印✨
// 双向链表打印
void ListPrint(ListNode* pHead)
{assert(pHead);ListNode* cur = pHead->next;printf("哨兵位 <--> ");while (cur != pHead){printf("%d <--> ", cur->data);cur = cur->next;}printf("哨兵位\n");
}
✨3.5 双向链表尾插✨
在进行插入节点之前,无论是头插还是尾插都需要申请一个新的节点,于是可以把此步骤成一个函数,减少代码的冗余。
//申请一个节点
ListNode* CreateLTNode(LTDataType x)
{ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL){perror("CreateLTNode --> malloc");return;}newnode->data = x;newnode->prev = NULL;newnode->next = NULL;return newnode;
}
- 首先,创建一个新的节点
newnode
,并将数据x
存储在其中。- 将
newnode
的prev
指针指向当前链表的第一个节点pHead
的前一个节点,即pHead->prev
。- 将
newnode
的next
指针指向当前链表的第一个节点pHead
。- 将当前链表的第一个节点
pHead
的前一个节点的next
指针指向新节点newnode
。- 将当前链表的第一个节点
pHead
的prev
指针指向新节点newnode
。通过以上步骤,新节点被插入到双向链表的尾部,并且链表中的其他节点仍然保持其原始顺序和链接关系。这样做可以确保新节点被正确地添加到链表中,并且不会破坏链表的结构。
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* newnode = CreateLTNode(x);//pHead pHead->prev newnodenewnode->prev = pHead->prev; newnode->next = pHead;pHead->prev->next = newnode;pHead->prev = newnode;
}
✨3.6 双向链表尾删✨
- 首先,获取链表的最后一个节点
tail
,它应该是头节点pHead
的前一个节点(即pHead->prev
)。- 接着,获取最后一个节点的前一个节点
tailPrev
。- 将头节点
pHead
的prev
指针指向最后一个节点的前一个节点tailPrev
,从而将最后一个节点从链表中间删除。- 将最后一个节点的前一个节点的
next
指针指向头节点pHead
,从而将头节点和最后一个节点连接起来。- 最后,释放最后一个节点的内存空间。
// 双向链表尾删
void ListPopBack(ListNode* pHead)
{assert(pHead);assert(pHead->next!=pHead);//链表不能为空ListNode* tail = pHead->prev;ListNode* tailPrev = tail->prev;// pHead tailPrev tailpHead->prev = tailPrev;tailPrev->next = pHead;free(tail);
}
✨3.7 双向链表头插✨
- 首先,创建一个新的节点
newnode
,并将数据x
存储在其中。- 将新节点的
prev
指针指向当前链表的第一个节点pHead
。- 将新节点的
next
指针指向当前链表的第一个节点的下一个节点,即pHead->next
。- 将当前链表的第一个节点的
next
指针指向新节点newnode
。- 将当前链表的第一个节点的下一个节点的
prev
指针指向新节点newnode
。通过以上步骤,新节点被插入到双向链表的头部,并且链表中的其他节点仍然保持其原始顺序和链接关系。这样做可以确保新节点被正确地添加到链表中,并且不会破坏链表的结构。
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* newnode = CreateLTNode(x);ListNode* rear = pHead->next;pHead->next = newnode;newnode->prev = pHead;newnode->next = rear;rear->prev = newnode;
}
✨3.8 双向链表头删✨
- 首先,获取链表的第一个节点
cur
,它应该是头节点pHead
的下一个节点(即pHead->next
)。- 将头节点的
next
指针指向第一个节点的下一个节点,从而将第一个节点从链表中间删除。- 将第一个节点的下一个节点的
prev
指针指向头节点pHead
,从而将头节点和第一个节点连接起来。- 最后,释放第一个节点的内存空间。
// 双向链表头删
void ListPopFront(ListNode* pHead)
{assert(pHead);assert(pHead->next != pHead);ListNode* cur = pHead->next;pHead->next = cur->next;cur->next->prev = pHead;free(cur);
}
✨3.9 双向链表查找✨
- 首先,从链表的第二个节点开始(即
pHead->next
),遍历链表的每个节点。- 对于每个节点,检查其存储的数据是否与要查找的数据
x
相等。- 如果找到了匹配的节点,则返回该节点。
- 如果遍历完整个链表都没有找到匹配的节点,则返回空指针(
NULL
)。
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* cur = pHead->next;while (cur != pHead){if (cur->data == x){return cur;}cur = cur->next;}return NULL;//如果没找到,返回空
}
✨3.10 双向链表在pos的前面进行插入✨
- 首先,创建一个新的节点
newnode
,并将数据x
存储在其中。- 获取要插入位置的前一个节点
_prev
。- 将前一个节点的
next
指针指向新节点newnode
。- 将新节点的
prev
指针指向前一个节点_prev
。- 将新节点的
next
指针指向当前节点pos
。- 将当前节点的
prev
指针指向新节点newnode
。通过以上步骤,新节点被插入到指定位置的前面,并且链表中的其他节点仍然保持其原始顺序和链接关系。这样做可以确保新节点被正确地添加到链表中,并且不会破坏链表的结构。
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);ListNode* newnode = CreateLTNode(x);ListNode* _prev = pos->prev;// _prev newnode pos_prev->next = newnode;newnode->prev = _prev;newnode->next = pos;pos->prev = newnode;
}
✨3.11 双向链表删除pos位置的节点✨
- 首先,确保要删除的节点
pos
不是空指针。- 获取要删除节点的前一个节点
_prev
和后一个节点rear
。- 将前一个节点的
next
指针指向后一个节点,从而将要删除的节点从链表中间删除。- 将后一个节点的
prev
指针指向前一个节点,从而将前一个节点和后一个节点连接起来。- 释放要删除的节点的内存空间。
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{assert(pos);ListNode* _prev = pos->prev;ListNode* rear = pos->next;_prev->next = rear;rear->prev = _prev;free(pos);
}
四、源代码
🌅4.1 List.h 文件
#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{LTDataType data; //节点存储的数据元素struct ListNode* next; //指向前驱节点struct ListNode* prev; //指向后继节点
}ListNode; //双链表结构// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);
🌅4.2 List.c 文件
#define _CRT_SECURE_NO_WARNINGS 1#include "List.h"//申请一个节点
ListNode* CreateLTNode(LTDataType x)
{ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL){perror("CreateLTNode --> malloc");return;}newnode->data = x;newnode->prev = NULL;newnode->next = NULL;return newnode;
}
// 创建返回链表的头结点.
ListNode* ListCreate()
{ListNode* head = (ListNode*)malloc(sizeof(ListNode));if (head == NULL){perror("ListCreate --> malloc");return;}head->data = -1;head->prev = head;head->next = head;return head;
}
// 双向链表销毁
void ListDestory(ListNode* pHead)
{assert(pHead);ListNode* cur = pHead->next;while (cur != pHead){ListNode* next = cur->next;free(cur);cur = next; }free(pHead);printf("双链表销毁成功!\n");
}
// 双向链表打印
void ListPrint(ListNode* pHead)
{assert(pHead);ListNode* cur = pHead->next;printf("哨兵位 <--> ");while (cur != pHead){printf("%d <--> ", cur->data);cur = cur->next;}printf("哨兵位\n");
}
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* newnode = CreateLTNode(x);//pHead pHead->prev newnodenewnode->prev = pHead->prev; newnode->next = pHead;pHead->prev->next = newnode;pHead->prev = newnode;
}
// 双向链表尾删
void ListPopBack(ListNode* pHead)
{assert(pHead);assert(pHead->next!=pHead);//链表不能为空ListNode* tail = pHead->prev;ListNode* tailPrev = tail->prev;// pHead tailPrev tailpHead->prev = tailPrev;tailPrev->next = pHead;free(tail);
}
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* newnode = CreateLTNode(x);ListNode* rear = pHead->next;pHead->next = newnode;newnode->prev = pHead;newnode->next = rear;rear->prev = newnode;
}
// 双向链表头删
void ListPopFront(ListNode* pHead)
{assert(pHead);assert(pHead->next != pHead);ListNode* cur = pHead->next;pHead->next = cur->next;cur->next->prev = pHead;free(cur);
}
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* cur = pHead->next;while (cur != pHead){if (cur->data == x){return cur;}cur = cur->next;}return NULL;//如果没找到,返回空
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);ListNode* newnode = CreateLTNode(x);ListNode* _prev = pos->prev;// _prev newnode pos_prev->next = newnode;newnode->prev = _prev;newnode->next = pos;pos->prev = newnode;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{assert(pos);ListNode* _prev = pos->prev;ListNode* rear = pos->next;_prev->next = rear;rear->prev = _prev;free(pos);
}
🌅4.3 Test.c 文件
#define _CRT_SECURE_NO_WARNINGS 1#include "List.h"void Test1()
{ListNode* plist = ListCreate();//尾插1、2、3ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPrint(plist);//头插5、4ListPushFront(plist, 5);ListPushFront(plist, 4);ListPrint(plist);//查找元素3,找到返回节点地址,没找到返回空ListNode* pos = ListFind(plist, 3);if (pos){printf("找到了\n");}else{printf("没找到\n");}//在3前面插入30ListInsert(pos, 30);ListPrint(plist); //删除3if (pos){ListErase(pos);pos = NULL;}ListPrint(plist);//尾删两次ListPopBack(plist);ListPopBack(plist);ListPrint(plist);//头删两次ListPopFront(plist);ListPopFront(plist);ListPrint(plist);//销毁链表ListDestory(plist);plist = NULL;
}int main()
{Test1();return 0;
}
🌅4.4 测试结果
相关文章:

带头双向循环链表:一种高效的数据结构
💓 博客主页:江池俊的博客⏩ 收录专栏:数据结构探索👉专栏推荐:✅cpolar ✅C语言进阶之路💻代码仓库:江池俊的代码仓库🔥编译环境:Visual Studio 2022🎉欢迎大…...

C++基础 -34- 输入输出运算符重载
输出运算符重载格式 ostream & operator<<(ostream &out,person a) {cout << a.a << endl;return out; }举例输出运算符重载 #include "iostream"using namespace std;class person {public:person(int a):a(a){}int a; };ostream &…...
MimicGen论文分析与资料汇总
MimicGen论文分析与资料汇总 前言论文分析相关资料汇总 前言 论文分析 相关资料汇总 Paper:MimicGen: A Data Generation System for Scalable Robot Learning using Human Demonstrations mimicgen.github 破局利刃!英伟达合成数据新成果:为机器人造…...
JAVA-每一页PDF转图片
结论:1、iText几乎找不到如何PDF转图片的信息,但能找到获取到PDF里面的图片并保存下来的信息;2、PDF box满大街都是参考代码(下面会附上一个作为参考);3、收费的库使用起来更简单,但就是要收费&…...

VS安装QT VS Tools编译无法通过
场景: 项目拷贝到虚拟机内部后,配置好相关环境后无法编译,安装QT VS Tools后依旧无法编译,查找资料网上说的是QT工具版本不一致导致的,但反复试了几个版本后依旧无法编译通过。错误信息如下: C:\Users\Ad…...

【C语言之 CJson】学CJson看这一篇就够了
文章目录 前言一、下载CJson二、创建一个json2.1 创建json对象cJSON类型详解 2.2 创建键值对2.3 添加嵌套的 JSON 对象2.4 添加数组创建数组添加元素到数组添加数组到obj 2.5 将 JSON 对象转为字符串2.6 释放内存2.7 示例代码 三、解析json3.1 解析json root3.2 把一个key解析出…...

使用Java语言实现字母之间的大小写转换
这个类的作用为实现字母之间的大小写转换,通过加减32来完成。 输入的代码 import java.util.Scanner; public class WordChangeDemo {public static void main(String[] args){try (Scanner in new Scanner(System.in)) {System.out.println("请输入您要进…...

Docker的数据持久化;Docker网络;Dockerfile编写
Docker的数据持久化;Docker网络;Dockerfile编写; 文章目录 Docker的数据持久化;Docker网络;Dockerfile编写;**Docker的数据持久化**1)将本地目录映射到容器里2)数据卷3)将…...

OpenHarmony亮相MTSC 2023 | 质量效率共进,赋能应用生态发展
11月25日,MTSC 2023第十二届中国互联网测试开发大会在深圳登喜路国际大酒店圆满举行。大会以“软件质量保障体系和测试研发技术交流”为主要目的,旨在为行业搭建一个深入探讨和交流的桥梁和平台。OpenAtom OpenHarmony(简称“OpenHarmony”&a…...

windows11 调整鼠标灵敏度方法
首先 我们打开电脑设置 或者在 此电脑/此计算机/我的电脑 右击选择属性 然后 有的电脑 左侧菜单中 直接就有 设备 然后在设备中直接就可以找到 鼠标 选项 调整光标速度即可 如果操作系统和我的一样 可以直接搜索鼠标 然后 选择 鼠标设置 然后 调整上面的鼠标指针速度即可...
贪心算法个人见解
目录 基本思想: 贪心算法的步骤: 示例: 贪心算法(Greedy Algorithm)是一种基于贪心策略的算法范式,它在每一步选择中都采取当前状态下的最优选择,而不考虑全局最优解。贪心算法通常适用于那些…...

Win中Redis部署与配置
1.下载msi版本 下载传送门 2.双击next-->next安装安装 3.密码配置以及开机自启 在配置文件中配置相应配置进行配置密码以及端口和ip port 6379指定 Redis 监听端口,默认端口为 6379,作者在自己的一篇博文中解释了为什么选用 6379 作为默认端口&…...
vue el-button 封装及使用
使用了 Element UI 中的 el-button 组件,并对其进行了封装和定制。 创建组件index.vue (src/common-ui/button/index.vue) <template><el-buttonclass"h-button":type"type":icon"hIcon":disabled"disabled"clic…...
QT之QMediaPlayer的用法
QT之QMediaPlayer的用法 成员函数例程 成员函数 1)setMedia(const QMediaContent &media, QIODevice *stream nullptr) 设置要播放的媒体内容,其中参数media指定了媒体内容,stream参数指定了用于读取媒体的输入设备(如文件流࿰…...

TCP_报文格式解读
报文格式 header部分字段含义解析 固定字段 对于header中固定部分字段含义,见之前的blog《TCP报文分析》; 对部分字段含义补充说明 Data Offset:4bit,tcp header的长度,单位:32bit(4字节&…...
C语言面试之旅:掌握基础,探索深度(面试实战之c语言关键词下篇)
一.枚举( enum) 枚举是 C 语言中的一种基本数据类型,用于定义一组具有离散值的常量,它可以让数据更简洁,更易读。枚举类型通常用于为程序中的一组相关的常量取名字,以便于程序的可读性和维护性。定义一个枚…...
Java学习第十三天
Java多态 多态是同一个行为具有多个不同表现形式或形态的能力。 多态就是同一个接口,使用不同的实例而执行不同操作 多态性是对象多种表现形式的体现。 多态的优点 1. 消除类型之间的耦合关系2. 可替换性3. 可扩充性4. 接口性5. 灵活性6. 简化性 多态存在的三个…...

【Delphi】实现彩色日志显示框(TRichEdit Helper)
目录 一、前言 二、实现方法 1. 第一步 2. 第二步 3. 第三步 三、主程序代码 四、下载 1. 可执行程序 2. 程序源代码 一、前言 在用Delphi做日常开发的时候,经常需要显示程序运行的日志,一般我们会使用TMemo,使用起来简单,…...

Elasticsearch 优化查询中获取字段内容的方式,性能提升5倍!
1、背景 集群配置为:8 个 node 节点,16 核 32G,索引 4 分片 1 副本。应用程序的查询逻辑是按经纬度排序后找前 200 条文档。 1、应用对查询要求比较高,search 没有慢查询的状态。 2、集群压测性能不能上去,cpu 使用未打…...

图像批量设计软件Retrobatch Pro mac中文版功能特色
Retrobatch Mac是一款灵活的批量图像处理工具。用户可以自由创建Workflow来实现相应的功能,这些Workflow能取代大量的重复劳动,提高生产力。Retrobatch Mac的一般操作是从左边栏拖动相应动作到工作区形成节点(Nodes),节…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...

ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...