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

【数据结构】双链表

大家好!今天我们来学习数据结构中的双链表。(我们这里讲解的是带头(哨兵位)双向循环链表哦~)

目录

1.双链表的概念

2. 双链表的逻辑结构

3. 双链表的定义

4. 双链表的接口实现

4.1 动态申请一个新结点

4.2 双链表的初始化

4.3 打印双链表

4.4 尾插数据

4.5 尾删数据

4.6 头插数据

4.7 头删数据

4.8 获得双链表的长度

4.9 查找指定数据

4.10 在指定位置之前插入数据

4.11 删除指定位置

4.12 销毁双链表

5. 双链表的完整代码

5.1 List.h

5.2 List.c

5.3 Test.c

6. 顺序表和链表的区别

7. 总结


1.双链表的概念

双链表也叫双向链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双链表中的任意一个结点开始,都可以很方便地访问它的前驱结点后继结点

2. 双链表的逻辑结构

我们这里以带头双向循环链表为例,它的逻辑结构如下:

3. 双链表的定义

typedef int LTDataType;
typedef struct ListNode
{LTDataType data;  //存储的数据struct ListNode* prev;  //存放前一个结点的地址struct ListNode* next;  //存放后一个结点的地址
}LTNode;

使用结构体创建一个双链表。

SLTDataType替换int,方便对不同类型的数据进行修改。

SLTNode替换struct SListNode,方便简洁。

data是结点的数据域*prev用来存放前一个结点的地址(前驱)*next用来存放后一个结点的地址(后继)

4. 双链表的接口实现

双链表的所有接口函数一览:

//动态申请一个新结点
LTNode* BuyLTNode(LTDataType x);
//双链表的初始化
LTNode* LTInit();
//打印双链表
void LTPrint(LTNode* phead);
//尾插
void LTPushBack(LTNode* phead, LTDataType x);
//尾删
void LTPopBack(LTNode* phead);
//头插
void LTPushFront(LTNode* phead, LTDataType x);
//头删
void LTPopFront(LTNode* phead);
//获得双链表的长度
int LTSize(LTNode* phead);
//查找
LTNode* LTFind(LTNode* phead, LTDataType x);
//在pos位置之前插入x
void LTInsert(LTNode* pos,LTDataType x);
//删除pos位置
void LTErase(LTNode* pos);
//销毁双链表
void LTDestroy(LTNode* phead);

这些接口函数主要实现了单链表的增删改查等功能,接下来我们一一实现这些函数!

4.1 动态申请一个新结点

我们每次给链表插入数据时,都需要动态开辟空间申请结点。所以我们将这个过程封装成函数,方便后续使用。

我们使用malloc()函数动态开辟一块空间表示新结点newnode,malloc函数返回一个void*类型的指针,指向分配的内存块的起始位置。如果内存分配失败,则返回一个空指针NULL。

所以我们要判断newnode是否为空指针NULL如果newnode是空指针,则用perror()函数打印相关错误,并用exit(-1)退出程序

如果newnode不为空,我们就用newnodedata赋值。又因为这是新开辟的结点,我们暂时将newnodeprevnewnodenext指向空

//动态申请一个新结点
LTNode* BuyLTNode(LTDataType x)
{LTNode* newnode = (LTNode *)malloc(sizeof(LTNode));if (newnode == NULL){perror("malloc failed");exit(-1);}newnode->data = x;newnode->next = NULL;newnode->prev = NULL;return newnode;
}

4.2 双链表的初始化

双链表的初始化,就是给双链表创建一个头结点。因为头结点(哨兵位)不存储有效数据,所以我们将头结点的data赋值为-1,同时让头结点的prev和next都指向自己,最后返回头结点的地址。

//双链表的初始化
LTNode* LTInit()
{LTNode* phead = BuyLTNode(-1);phead->next = phead;phead->prev = phead;return phead;
}

4.3 打印双链表

遍历双链表,依次打印双链表的元素。

我们定义一个结构体类型的指针cur,让cur一开始指向头结点的下一个结点(也就是哨兵位后面的一个结点)。当cur不为空时,输出cur指向的结点的值(cur->data),然后让cur指向下一个结点(cur=cur->next),依次进行,直到cur为头结点时停止(因为最后一个结点的next指针指向头结点)

//打印双链表
void LTPrint(LTNode* phead)
{assert(phead);LTNode* cur = phead->next;printf("phead<=>");while (cur != phead){printf("%d<=>", cur->data);cur = cur->next;}printf("\n");
}

4.4 尾插数据

尾插,就是创建一个新结点newnode,然后将newnode插入到尾结点tail的后面,让tail的next指向newnode,让newnode的prev指向tail;让newnode的next指向头结点phead头结点phead的prev指向newnode。建立这样的连接后,尾插就完成了

//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* tail = phead->prev;LTNode* newnode = BuyLTNode(x);tail->next = newnode;newnode->prev = tail;newnode->next = phead;phead->prev = newnode;
}

4.5 尾删数据

尾删,我们需要定义一个tailPrev存储尾结点tail的前一个结点(也就是tail->prev),再free掉tail,让tailPrev的next指向头结点phead,让头结点phead的prev指向tailPrev

这里要注意的是,如果链表为空(phead->next==phead),我们就不能进行尾删,所以我们要用assert()进行断言。

//尾删
void LTPopBack(LTNode* phead)
{assert(phead);assert(phead->next!= phead); //链表为空的情况LTNode* tail = phead->prev;LTNode* tailPrev = tail->prev;free(tail);tailPrev->next = phead;phead->prev = tailPrev;
}

4.6 头插数据

所谓头插,就是在双链表的头结点(哨兵位)后面的一个结点前插入数据。我们调用BuyLTNode()函数创建一个新结点newnode,让newnode的next指向头结点phead的next头结点phead的next的prev指向newnode;让头结点phead的next指向newnodenewnode的prev指向phead

//头插
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = BuyLTNode(x);newnode->next = phead->next;phead->next->prev = newnode;phead->next = newnode;newnode->prev = phead;
}

4.7 头删数据

头删,就是将头结点(哨兵位)后面的那一个结点删除。这里我们可以用first存储头结点(哨兵位)后面的第一个结点,用second存储哨兵位后面的第二个结点。然后free掉first。将头结点phead的next指向second而second的prev指向头结点

这里也要注意,如果链表为空(phead->next==phead),我们就不能进行头删,所以我们要用assert()进行断言。

​ 

//头删
void LTPopFront(LTNode* phead)
{assert(phead);assert(phead->next != phead);  //链表为空的情况LTNode* first = phead->next;LTNode* second = phead->next->next;free(first);phead->next = second;second->prev = phead;
}

4.8 获得双链表的长度

要获得双链表的长度,我们就使用cur从头结点(哨兵位)的后一个结点开始遍历,直到cur等于头结点phead时停止

//获得双链表的长度
int LTSize(LTNode* phead)
{assert(phead);int size = 0;LTNode* cur = phead->next;while (cur != phead){++size;cur = cur->next;}return size;
}

4.9 查找指定数据

定义一个结构体指针cur,让cur首先指向头结点(哨兵位)的下一个结点,然后遍历双链表,如果找到了指定数据cur->data==x),就直接返回cur。否则让cur指向cur->next,直到cur为头结点时停止。如果没有提前退出,完整完成了整个循环(也就是没有找到指定数据),就返回空指针NULL

//查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);LTNode* cur = phead->next;while (cur != phead){if (cur->data == x)return cur;cur = cur->next;}return NULL;
}

4.10 在指定位置之前插入数据

我们调用BuyLTNode()函数创建一个新结点newnode,定义一个结构体指针posPrev用来保存pos位置的前一个位置,让posPrev的next指向newnodenewnode的prev指向posPrev;让newnode的next指向pospos的prev指向newnode

既然我们要在指定位置之前插入数据,那么这个指定位置必须是存在的,所以我们要使用assert()断言。

//在pos位置之前插入x
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* posPrev = pos->prev;LTNode* newnode = BuyLTNode(x);posPrev->next = newnode;newnode->prev = posPrev;newnode->next = pos;pos->prev = newnode;
}

4.11 删除指定位置

我们要删除指定位置,可以定义一个结构体指针posPrev保存要删除位置的前一个位置,定义一个结构体指针posNext保存要删除位置的后一个位置。然后free掉posposPrev的next指向posNextposNext的prev指向posPrev

既然要删除指定位置,那么这个指定位置也必须是存在的,这里也同样要用assert()断言。

//删除pos位置
void LTErase(LTNode* pos)
{assert(pos);LTNode* posPrev = pos->prev;LTNode* posNext = pos->next;free(pos);posPrev->next = posNext;posNext->prev = posPrev;
}

4.12 销毁双链表

我们先让cur指向头结点(哨兵位)的下一个结点,然后遍历双链表,定义一个结构体指针next用来保存遍历时每一个结点的后面一个结点,依次free每个结点然后让cur指向next,直到cur指向头结点时停止

最后将头结点(哨兵位)phead释放

//销毁双链表
void LTDestroy(LTNode* phead)
{assert(phead);LTNode* cur = phead->next;while (cur != phead){LTNode* next = cur->next;free(cur);cur = next;}free(phead);
}

5. 双链表的完整代码

5.1 List.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int LTDataType;
typedef struct ListNode
{LTDataType data;  //存储的数据struct ListNode* prev;  //指向前一个结点的指针struct ListNode* next;  //指向后一个结点的指针
}LTNode;//动态申请一个新结点
LTNode* BuyLTNode(LTDataType x);
//双链表的初始化
LTNode* LTInit();
//打印双链表
void LTPrint(LTNode* phead);
//尾插
void LTPushBack(LTNode* phead, LTDataType x);
//尾删
void LTPopBack(LTNode* phead);
//头插
void LTPushFront(LTNode* phead, LTDataType x);
//头删
void LTPopFront(LTNode* phead);
//获得双链表的长度
int LTSize(LTNode* phead);
//查找
LTNode* LTFind(LTNode* phead, LTDataType x);
//在pos位置之前插入x
void LTInsert(LTNode* pos,LTDataType x);
//删除pos位置
void LTErase(LTNode* pos);
//销毁双链表
void LTDestroy(LTNode* phead);

5.2 List.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"//动态申请一个新结点
LTNode* BuyLTNode(LTDataType x)
{LTNode* newnode = (LTNode *)malloc(sizeof(LTNode));if (newnode == NULL){perror("malloc failed");exit(-1);}newnode->data = x;newnode->next = NULL;newnode->prev = NULL;return newnode;
}//双链表的初始化
LTNode* LTInit()
{LTNode* phead = BuyLTNode(-1);phead->next = phead;phead->prev = phead;return phead;
}//打印双链表
void LTPrint(LTNode* phead)
{assert(phead);LTNode* cur = phead->next;printf("phead<=>");while (cur != phead){printf("%d<=>", cur->data);cur = cur->next;}printf("\n");
}//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* tail = phead->prev;LTNode* newnode = BuyLTNode(x);tail->next = newnode;newnode->prev = tail;newnode->next = phead;phead->prev = newnode;
}//尾删
void LTPopBack(LTNode* phead)
{assert(phead);assert(phead->next!= phead); //链表为空的情况LTNode* tail = phead->prev;LTNode* tailPrev = tail->prev;free(tail);tailPrev->next = phead;phead->prev = tailPrev;
}//头插
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = BuyLTNode(x);newnode->next = phead->next;phead->next->prev = newnode;phead->next = newnode;newnode->prev = phead;
}//头删
void LTPopFront(LTNode* phead)
{assert(phead);assert(phead->next != phead);  //链表为空的情况LTNode* first = phead->next;LTNode* second = phead->next->next;free(first);phead->next = second;second->prev = phead;
}//获得双链表的长度
int LTSize(LTNode* phead)
{assert(phead);int size = 0;LTNode* cur = phead->next;while (cur != phead){++size;cur = cur->next;}return size;
}//查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);LTNode* cur = phead->next;while (cur != phead){if (cur->data == x)return cur;cur = cur->next;}return NULL;
}//在pos位置之前插入x
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* posPrev = pos->prev;LTNode* newnode = BuyLTNode(x);posPrev->next = newnode;newnode->prev = posPrev;newnode->next = pos;pos->prev = newnode;
}//删除pos位置
void LTErase(LTNode* pos)
{assert(pos);LTNode* posPrev = pos->prev;LTNode* posNext = pos->next;free(pos);posPrev->next = posNext;posNext->prev = posPrev;
}//销毁双链表
void LTDestroy(LTNode* phead)
{assert(phead);LTNode* cur = phead->next;while (cur != phead){LTNode* next = cur->next;free(cur);cur = next;}free(phead);
}

5.3 Test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"void TestList()
{LTNode* plist = LTInit();LTPushBack(plist, 1);LTPushBack(plist, 2);LTPushBack(plist, 3);LTPushBack(plist, 4);LTPushBack(plist, 5);LTPrint(plist);LTPushFront(plist, 10);LTPushBack(plist, 10);LTPrint(plist);LTPopBack(plist);LTPopFront(plist);LTPrint(plist);LTPushFront(plist, 10);LTPushFront(plist, 20);LTPushFront(plist, 30);LTPushFront(plist, 40);LTPrint(plist);LTPopFront(plist);LTPrint(plist);LTPopBack(plist);LTPrint(plist);LTDestroy(plist);plist = NULL;
}

6. 顺序表和链表的区别

存储器层次结构:

顺序表

优点:下标随机访问,cpu高速缓存命中率高

缺点:头部和中间插入删除效率低,扩容有一定程度性能消耗,可能存在一定程度的空间浪费。

链表

优点:可以任意位置插入删除,复杂度O(1),能够按需申请释放。

缺点:不支持下标随机访问。

7. 总结

到这里,我们就用C语言实现了数据结构中的双链表。有什么问题欢迎在评论区讨论。如果觉得文章有什么不足之处,可以在评论区留言。如果喜欢我的文章,可以点赞收藏哦!

相关文章:

【数据结构】双链表

大家好&#xff01;今天我们来学习数据结构中的双链表。&#xff08;我们这里讲解的是带头&#xff08;哨兵位&#xff09;双向循环链表哦~&#xff09; 目录 1.双链表的概念 2. 双链表的逻辑结构 3. 双链表的定义 4. 双链表的接口实现 4.1 动态申请一个新结点 4.2 双链表…...

android设置竖屏仍然跟随屏幕旋转怎么办

如题所问&#xff0c;我最近遇到一个bug&#xff0c;就是设置了摇感&#xff0c;然后有用户反馈说设置了手机下拉的系统设置-屏幕旋转-关闭。然后屏幕还是会旋转的问题。 首先&#xff0c;我们先从如何设置横竖屏了解下好了 设置横屏和竖屏的方法&#xff1a; 方法一&#x…...

java spring cloud 企业电子招标采购系统源码:营造全面规范安全的电子招投标环境,促进招投标市场健康可持续发展 tbms

​ 项目说明 随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大&#xff0c;公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境&#xff0c;最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范&#xff0c;以…...

【Java】2021 RoboCom 机器人开发者大赛-高职组(初赛)题解

7-1 机器人打招呼 机器人小白要来 RoboCom 参赛了&#xff0c;在赛场中遇到人要打个招呼。请你帮它设置好打招呼的这句话&#xff1a;“ni ye lai can jia RoboCom a?”。 输入格式&#xff1a; 本题没有输入。 输出格式&#xff1a; 在一行中输出 ni ye lai can jia Robo…...

汽车制造业上下游协作时 外发数据如何防泄露?

数据文件是制造业企业的核心竞争力&#xff0c;一旦发生数据外泄&#xff0c;就会给企业造成经济损失&#xff0c;严重的&#xff0c;可能会带来知识产权剽窃损害、名誉伤害等。汽车制造业&#xff0c;会涉及到重要的汽车设计图纸&#xff0c;像小米发送汽车设计图纸外泄事件并…...

H13-922题库 HCIP-GaussDB-OLAP V1.5

**H13-922 V1.5 GaussDB(DWS) OLAP题库 华为认证GaussDB OLAP数据库高级工程师HCIP-GaussDB-OLAP V1.0自2019年10月18日起&#xff0c;正式在中国区发布。当前版本V1.5 考试前提&#xff1a; 掌握基本的数据库基础知识、掌握数据仓库运维的基础知识、掌握基本Linux运维知识、…...

美团视觉GPU推理服务部署架构优化实战

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…...

什么是前端框架?怎么学习? - 易智编译EaseEditing

前端框架是一种用于开发Web应用程序界面的工具集合&#xff0c;它提供了一系列预定义的代码和结构&#xff0c;以简化开发过程并提高效率。 前端框架通常包括HTML、CSS和JavaScript的库和工具&#xff0c;用于构建交互式、动态和响应式的用户界面。 学习前端框架可以让您更高效…...

logstash 原理(含部署)

1、ES原理 原理 使⽤filebeat来上传⽇志数据&#xff0c;logstash进⾏⽇志收集与处理&#xff0c;elasticsearch作为⽇志存储与搜索引擎&#xff0c;最后使⽤kibana展现⽇志的可视化输出。所以不难发现&#xff0c;⽇志解析主要还 是logstash做的事情 从上图中可以看到&#x…...

CSS中的position属性有哪些值,并分别描述它们的作用。

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ static⭐ relative⭐ absolute⭐ fixed⭐ sticky⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那…...

视频联网报警厂家怎么找?

视频联网报警厂家怎么找&#xff1f;要找到联网报警设备厂家&#xff0c;可以按照以下步骤进行&#xff1a; 1. 在互联网上搜索&#xff1a;可以使用搜索引擎&#xff0c;如谷歌或百度&#xff0c;搜索关键词&#xff0c;如“联网报警设备厂家”、“安防设备厂家”等&#xff…...

配置文件优先级解读

目录 概述 同级目录application配置文件优先级 application 以及bootstrap 优先级 不同级目录配置文件优先级 外部配置加载顺序 概述 SpringBoot除了支持properties格式的配置文件&#xff0c;还支持另外两种格式的配置文件。三种配置文件格式分别如下: properties格式…...

在 React+Typescript 项目环境中创建并使用组件

上文 ReactTypescript清理项目环境 我们将自己创建的项目环境 好好清理了一下 下面 我们来看组件的创建 组件化在这种数据响应式开发中肯定是非常重要的。 我们现在src下创建一个文件夹 叫 components 就用他专门来处理组件业务 然后 我们在下面创建一个 hello.tsx 注意 是t…...

UNIAPP中开发企业微信小程序

概述 需求为使用uni-app开发企业微信小程序。希望可以借助现成的uni-app框架&#xff0c;快速开发。遇到的问题是uni-app引入jweixin-1.2.0.js提示异常: Reason: TypeError: Cannot read properties of undefined (reading ‘title’)。本文中描述了如何解决该问题&#xff0c…...

NGINX负载均衡及LVS-DR负载均衡集群

目录 LVS-DR原理搭建过程nginx 负载均衡 LVS-DR原理 原理&#xff1a; 1. 当用户向负载均衡调度器&#xff08;Director Server&#xff09;发起请求&#xff0c;调度器将请求发往至内核空间 2. PREROUTING链首先会接收到用户请求&#xff0c;判断目标IP确定是本机IP&#xff…...

由于目标计算机积极拒绝,无法连接。 Could not connect to Redis at 127.0.0.1:6379

项目在启动时候报出redis连接异常 然后查看是redis 连接被计算机拒绝 解决方法 打开redis安装文件夹 先打开redis-servce.exe挂着&#xff0c;再打开redis-cli.exe 也不会弹出被拒接的问题了。而且此方法不用每次都去cmd里输入命令。...

电脑提示数据错误循环冗余检查怎么办?

有些时候&#xff0c;我们尝试在磁盘上创建分区或清理硬盘时&#xff0c;还可能会遇到这个问题&#xff1a;数据错误循环冗余检查。这是如何导致的呢&#xff1f;我们又该如何解决这个问题呢&#xff1f;下面我们就来了解一下。 导致冗余检查错误的原因有哪些&#xff1f; 数据…...

剑指offer62.圆圈中最后剩下的数字

这道题在算法课上的一个小故事上有一个类似的&#xff0c;就是一个军官打了败仗&#xff0c;带着他的几个兵逃到一个山洞&#xff0c;他们不想当俘虏想自杀&#xff0c;但是军官不想自杀但是又不好意思走&#xff0c;于是军官想了个办法&#xff0c;他们几个人围成一个圈&#…...

Python分享之 Spider

一、网络爬虫 网络爬虫又被称为网络蜘蛛&#xff0c;我们可以把互联网想象成一个蜘蛛网&#xff0c;每一个网站都是一个节点&#xff0c;我们可以使用一只蜘蛛去各个网页抓取我们想要的资源。举一个最简单的例子&#xff0c;你在百度和谷歌中输入‘Python&#xff0c;会有大量和…...

Golang项目中如何轻松实现私有仓库pkg包的引入

在企业内部创建一个公共的Golang模块工程可以帮助提高代码复用性和开发效率。本文将从如何创建一个公共的Golang工程开始&#xff0c;指导你一步步创建它、并引入到你的工程中。 1、公共模块规范 下面是一个简单的步骤指南来创建这样一个公共模块项目。 创建版本控制仓库&am…...

ElevenLabs广西话输出突然失真?一文定位3类隐藏错误:声母浊化丢失、入声韵尾截断、连读变调失效

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;ElevenLabs广西话语音输出失真现象概览 ElevenLabs 作为当前主流的AI语音合成平台&#xff0c;其多语言支持能力广受开发者青睐。然而&#xff0c;在针对广西话&#xff08;粤语勾漏片与邕浔片混合变体…...

Keil µVision TAB显示异常问题分析与解决方案

1. 问题现象与背景分析在Keil Vision集成开发环境中&#xff0c;部分用户遇到了编辑器界面显示异常的问题。具体表现为&#xff1a;当代码中包含TAB字符&#xff08;制表符&#xff09;时&#xff0c;屏幕上会出现奇怪的显示错乱&#xff0c;原本应该显示为空白缩进的区域&…...

FastbootEnhance:Windows平台终极Fastboot工具箱与Payload提取器完整指南

FastbootEnhance&#xff1a;Windows平台终极Fastboot工具箱与Payload提取器完整指南 【免费下载链接】FastbootEnhance A user-friendly Fastboot ToolBox & Payload Dumper for Windows 项目地址: https://gitcode.com/gh_mirrors/fa/FastbootEnhance 你是否曾经因…...

从零开发游戏需要学习的c#模块,第二十章(2D 敌人与战斗触发)

本节课我们要学习的内容在地图上随机生成红色敌人玩家碰到敌人后&#xff0c;进入战斗模式战斗胜利后敌人消失&#xff0c;获得分数屏幕显示敌人数量using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Syst…...

模拟几种数据融合协作频谱感知技术在认知无线电应用中性能研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

MVVMFramework性能优化:让你的iOS应用运行如飞的10个技巧

MVVMFramework性能优化&#xff1a;让你的iOS应用运行如飞的10个技巧 【免费下载链接】MVVMFramework (OC版)总结整理下一个快速开发框架&#xff0c;以更优雅的方式写代码&#xff0c;做一个代码艺术家。分离控制器中的代码&#xff0c;已加入cell自适应高度&#xff0c;自动缓…...

从塑造品牌形象到沉淀行业公信力软文营销品效合一落地路径及平台选择技巧

当下企业软文营销已经告别只追求表面曝光的初级阶段,进入品牌背书流量曝光线索转化品效合一的成熟时代。单纯追求发稿数量、追求媒体覆盖面,无法为企业带来实际商业价值;只有打通内容传播、品牌信任、受众触达、咨询引流的完整链路,让软文既能塑造品牌形象、沉淀行业公信力,又能…...

Go Web中间件机制深度剖析与实战

Go Web中间件机制深度剖析与实战 引言 中间件&#xff08;Middleware&#xff09;是Web开发中的核心概念&#xff0c;它在请求处理链路中扮演着至关重要的角色。本文将深入探讨Go语言中中间件的实现机制&#xff0c;并通过实战案例展示如何构建可复用的中间件系统。 一、中间件…...

Cursor Pro激活工具深度解析:机器ID重置与多账户管理的技术实现

Cursor Pro激活工具深度解析&#xff1a;机器ID重置与多账户管理的技术实现 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reached…...

鬼谷八荒2026官方正版最新版pc免费下载(看到请立即转存 资源随时失效)手机版通用

下载链接 逆天改命与八荒求道&#xff1a;解析《鬼谷八荒》的幕后历程、核心玩法与行业对比 在近年来的国产独立游戏浪潮中&#xff0c;修仙题材始终占据着举足轻重的地位。而在众多作品里&#xff0c;《鬼谷八荒》凭借其独特的画风与开放世界沙盒的定位&#xff0c;一度引发了…...