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

时间复杂度和空间复杂度 part2

一,空间复杂度

空间复杂度是衡量一个算法在执行过程中所需内存空间的量度。它反映了算法随着输入数据规模(通常是 nn)的增加,所消耗的内存量如何变化。空间复杂度是分析算法效率的一个重要方面,尤其是在内存资源有限的环境中,如嵌入式系统、移动设备等。

空间复杂度的定义

空间复杂度指的是算法在执行过程中所使用的额外空间,包括以下几类空间:

  1. 输入数据空间:这是存储输入数据所占的空间,通常在空间复杂度的计算中不考虑,假设输入数据已经提供。
  2. 辅助空间:即算法在执行过程中需要额外申请的空间(除了输入数据外),包括:
    • 临时变量
    • 数据结构(如数组、栈、队列等)
    • 递归调用栈空间

常见的空间复杂度

空间复杂度通常以大 O 表示法来表示,和时间复杂度一样,用来描述算法所需要的内存空间随着输入数据规模的变化趋势。以下是一些常见的空间复杂度:

  1. O(1):常数空间复杂度,表示算法只使用固定的内存量,与输入数据的大小无关。举个例子,简单的数值计算、交换两个数等操作通常是 O(1) 空间复杂度。

  2. O(n):线性空间复杂度,表示算法的空间需求与输入数据的大小成线性关系。例如,复制一个数组或创建一个与输入数据大小相同的辅助数据结构(如数组或链表),通常需要 O(n) 的空间。

  3. O(n²):平方空间复杂度,表示算法的空间需求与输入数据的平方成正比。例如,某些矩阵操作(如二维数组的存储)可能需要 O(n²) 的空间。

  4. O(log n):对数空间复杂度,表示算法的空间需求与输入数据的对数成正比。例如,递归算法中,如果递归的深度为对数级别,那么它的空间复杂度通常为 O(log n)。

空间复杂度的影响因素

空间复杂度不仅仅取决于输入数据的大小,还与算法内部使用的数据结构、存储方式、递归的深度等因素有关。以下是影响空间复杂度的一些因素:

  1. 数据结构的使用:不同的数据结构占用的内存空间不同。例如,使用链表存储数据通常比数组占用更多的空间,因为链表需要额外存储指针,而数组只存储数据。
  2. 递归深度:递归算法会占用栈空间,递归的深度越大,空间复杂度也越大。例如,递归树的深度与输入数据的规模相关时,可能会导致 O(n) 或 O(log n) 的空间复杂度。
  3. 辅助数据存储:一些算法需要临时存储中间结果,这也会影响空间复杂度。例如,归并排序需要额外的数组来存储合并的中间结果,空间复杂度是 O(n)。

空间复杂度的计算方法

计算空间复杂度时,我们通常关心算法在执行过程中使用的额外空间,而不是输入数据所占用的空间。以下是几种常见的空间使用情形:

  • 常数空间(O(1)):如果算法使用的空间数量不依赖于输入数据的大小。例如,算法只使用几个临时变量来进行计算。

    示例:

     

    def sum_array(arr): total = 0 # 常数空间

  • for num in arr: total += num return total

  • 线性空间(O(n)):如果算法使用的数据结构的大小随着输入数据规模的增大而增大。例如,创建一个新的数组来存储输入数据的副本。

    示例:

     

    def copy_array(arr): new_arr = arr[:] # 新数组,空间复杂度为O(n) return new_arr

  • 递归空间(O(n) 或 O(log n)):递归算法通常会占用栈空间,递归深度越大,空间复杂度越大。例如,深度为 n 的递归算法会消耗 O(n) 空间,而深度为 log n 的递归算法会消耗 O(log n) 空间。

    示例:

     

    def factorial(n): if n == 1: return 1 return n * factorial(n - 1) # 空间复杂度是O(n),因为递归深度为n

总结

空间复杂度是衡量算法执行过程中所需内存空间的量度。它关注的是算法在执行期间占用的额外内存(除了输入数据所占空间外)。对于任何一个算法,我们都应该关注其空间复杂度,尤其是在内存资源有限的环境中。常见的空间复杂度包括 O(1)、O(n)、O(n²) 等,不同的算法和数据结构会导致不同的空间复杂度,设计高效的算法时,需要权衡时间和空间的消耗。

动态扩容

动态数组的扩容是动态数据结构(如 ArrayListVector 等)在其容量不足时自动增加空间以容纳更多元素的过程。与静态数组不同,动态数组的大小不是固定的,而是可以根据需要动态调整。在实际操作中,动态数组的扩容过程通常是通过重新分配更大的内存空间并将旧数据复制到新位置来实现的。

动态数组的基本操作

动态数组的核心特性是能够在不预先知道元素数量的情况下,动态调整数组的大小。大多数情况下,动态数组支持以下操作:

  • 插入元素:动态数组可以随时向其尾部添加元素。
  • 查找元素:可以根据索引快速访问数组中的元素。
  • 删除元素:可以删除特定元素,虽然删除操作通常会涉及元素的位移。
  • 扩容:当数组的大小不够时,动态数组会自动扩展其存储空间。
扩容的原理

动态数组扩容的目的是为了解决数组在插入新元素时可能遇到的容量不足问题。通常情况下,扩容是通过以下步骤实现的:

  1. 判断是否需要扩容:当动态数组的当前容量已满且再插入元素时,需要扩容。此时,通常会将数组的容量增加到原来的 2倍(或者有时是固定增量),以保持较高的空间利用率和操作效率。

  2. 重新分配内存:扩容时,动态数组会分配一个新的、更大的内存块,通常是原数组大小的两倍。

  3. 复制数据:将原数组中的所有元素复制到新的、更大的内存块中。

  4. 更新指针/引用:将新的数组指针(或者引用)赋给动态数组变量,原数组的内存空间就可以被垃圾回收(在一些语言中,如Java和Python)。

扩容的性能分析
  1. 扩容时的时间复杂度

    • 扩容操作本身需要 O(n) 时间,因为它涉及到将数组中的每个元素复制到新的内存空间。
    • 但值得注意的是,扩容并不会在每次插入时都发生,而是周期性地发生。具体来说,扩容通常是每次数组容量不足时,扩容到原来的 2 倍,因此在进行 n 次插入时,扩容操作平均需要的时间为 O(1)(摊销时间复杂度)。

    摊销分析:假设每次扩容操作都是将数组的大小翻倍,那么对于 n 次插入,扩容操作的总成本是:

    • 第一次扩容将数组大小从 1 扩展到 2(复制 1 个元素)
    • 第二次扩容将数组大小从 2 扩展到 4(复制 2 个元素)
    • 第三次扩容将数组大小从 4 扩展到 8(复制 4 个元素)
    • ...
    • 最后一轮扩容将数组大小从 2^k 扩展到 2^(k+1)(复制 2^k 个元素)

    因此,整个扩容操作的总成本是 1 + 2 + 4 + 8 + ... + n,这可以简化为 O(n)。当将这个总成本摊销到 n 次插入操作中时,平均每次插入的扩容时间复杂度为 O(1)

  2. 空间复杂度

    • 动态数组的空间复杂度为 O(n),其中 n 是数组当前存储的元素个数。因为动态数组需要存储实际的元素以及动态扩容后的额外空间(通常会分配多余空间以提高效率),所以其空间复杂度是与元素个数成线性关系的。
    • 如果扩容的策略是每次翻倍,可能会导致在某一时刻,数组的实际元素数小于数组的总容量,造成一定的空间浪费,但这种浪费通常是有限的。
何时扩容:容量和负载因子

扩容的时机和策略通常依赖于负载因子(load factor)。负载因子是指数组中已使用空间与数组总容量的比例,常见的负载因子约定为 0.75。这意味着,当数组的元素数量达到当前容量的 75% 时,就会触发扩容操作。

  • 例如,如果动态数组当前的容量是 10,当其中的元素数量达到 7 时,负载因子达到 0.75,这时就会触发扩容,将容量增大为原来的 2 倍,变为 20。

均摊时间复杂度

 
**均摊时间复杂度**(Amortized Time Complexity)是对一系列操作在最坏情况下的平均时间复杂度的分析方法。它用于描述在一系列操作中,某个操作的平均成本,而不仅仅是单个操作的最坏情况。均摊分析通常应用于那些**偶尔有昂贵操作**(如扩容)但大多数操作是便宜的算法或数据结构。

### 1. **什么是均摊时间复杂度**

均摊时间复杂度是指通过将多个操作的总时间复杂度平均到每一个操作上,从而得出操作的平均成本。它帮助我们理解在长期内进行大量操作时,每个操作的“平均”成本,而不仅仅是单个操作的最坏情况。

举个例子,在 **动态数组扩容** 这样的场景中,扩容本身可能是一个昂贵的操作(O(n)),但是在多个操作中,如果扩容是偶尔发生的,它的影响会被“摊销”到其他便宜的操作上,使得每个操作的均摊复杂度变得更低。

### 2. **均摊时间复杂度的例子:动态数组扩容**

动态数组(如 `ArrayList` 或 `Vector`)的扩容机制通常会导致偶尔发生昂贵的操作——即当数组满时,必须将数据复制到更大的内存中。这种扩容操作的时间复杂度为 **O(n)**,但是这种情况并不是每次插入操作都会发生的。

假设动态数组的容量为 `n`,当它达到 `n` 个元素时,插入第 `n+1` 个元素会触发扩容。扩容操作将数组大小翻倍,并将所有 `n` 个元素复制到新数组中。

如果我们对 `n` 次插入操作进行分析,第一次扩容操作的时间复杂度是 **O(n)**,第二次扩容时的时间复杂度是 **O(2n)**,依此类推。看起来这很昂贵,但实际上,扩容操作并不会每次都发生,因此可以通过**摊销**计算均摊时间复杂度。

### 3. **均摊时间复杂度的计算**

考虑一个有 `n` 次插入操作的动态数组,扩容的发生规律是容量每次翻倍。虽然扩容的时间复杂度为 **O(n)**,但因为它是每次容量达到当前容量的上限时才发生一次,并且扩容的次数相对较少,所以我们可以用均摊时间复杂度来计算。

假设我们插入 `n` 个元素:

- **第一次扩容**:当插入第一个元素时,不需要扩容。当插入第 `n+1` 个元素时,需要扩容,复制 `n` 个元素到新的数组,时间复杂度为 **O(n)**。
- **第二次扩容**:当数组容量达到 `2n` 时,插入第 `2n+1` 个元素时,需要扩容,复制 `2n` 个元素,时间复杂度为 **O(2n)**。
- **以此类推**,每次扩容的时间复杂度为原来的一倍。

总的时间复杂度是:

$$
O(1) + O(1) + \cdots + O(1) + O(n) + O(2n) + O(4n) + \cdots
$$

这个和是一个几何级数,摊销下来,整个插入操作的总时间复杂度为 **O(n)**,而均摊到每次插入,时间复杂度为 **O(1)**。

### 4. **为什么要使用均摊分析**

- **反映真实性能**:均摊分析能够更准确地反映实际操作的性能,特别是对于那些偶尔需要昂贵操作的数据结构。例如,动态数组虽然扩容是昂贵的,但在插入大量元素时,大多数插入操作的时间复杂度是常数级别的 **O(1)**。
- **避免过度优化**:最坏情况分析往往很保守,无法准确描述实际的性能。在实际应用中,大多数操作都是常数时间复杂度的,如果过度关注最坏情况复杂度,可能会导致对性能的误判,均摊时间复杂度则能够提供更实际的视角。
- **分析复杂数据结构**:对于某些数据结构,如 `splay tree`、`hash table` 等,均摊分析能够有效计算实际操作的平均性能,帮助我们了解在多次操作下的表现。

### 5. **均摊分析的具体类型**

均摊时间复杂度通常有几种常见的分析方法,具体选择哪种方法取决于数据结构和算法的特性。

- **聚合方法(Aggregate Method)**:这种方法是最直接的,直接计算一系列操作的总时间,然后将总时间平均到每个操作上。适用于每个操作有相似的成本,或者对每个操作都能进行较为精确的成本估计。
  
- **会计方法(Accounting Method)**:假设每个操作有一个“实际成本”和“虚拟成本”,其中某些操作会提前“支付”额外的成本,提前为昂贵操作储备费用。通过这种方法,我们可以在某些操作上“存钱”来补偿未来的昂贵操作。
  
- **潜力方法(Potential Method)**:通过引入一个潜力函数,将数据结构的状态与其潜力(类似于虚拟成本)关联起来。潜力函数可以通过降低结构复杂度来降低未来操作的成本。

### 6. **总结**

均摊时间复杂度是对一系列操作的平均时间复杂度的分析方法,它帮助我们理解在长期操作中,每个操作的“平均”成本。通过均摊分析,复杂度较高的操作(如扩容)能够被“摊销”到其他便宜的操作上,从而得出更加准确的时间复杂度估计。它对于那些偶尔发生昂贵操作但大多数操作便宜的情况(例如动态数组的扩容)非常有用,能够更真实地反映程序的性能。

不要通过代码结构去判断复杂度

1,通常对于循环n次的for循环,如果再嵌套一个相同的for循环,时间复杂度会是n平方,但是有例外的,如

int n;
for (int i = 0; i < n; i++)
{for (int j = i; j < n; j += i);
}

时间复杂度就是n/1 + n/2 + n/3 +……+n/n,属于调和级数,是对数型的增长。

2,调和级数

 

要了解算法流程。

相关文章:

时间复杂度和空间复杂度 part2

一&#xff0c;空间复杂度 空间复杂度是衡量一个算法在执行过程中所需内存空间的量度。它反映了算法随着输入数据规模&#xff08;通常是 nn&#xff09;的增加&#xff0c;所消耗的内存量如何变化。空间复杂度是分析算法效率的一个重要方面&#xff0c;尤其是在内存资源有限的…...

【电机控制器】STC8H1K芯片——UART串口通信

【电机控制器】STC8H1K芯片——UART串口通信 文章目录 [TOC](文章目录) 前言一、UART1.串口初始化2.串口中断3.发送一个字节 二、实验1.原理图2.实验现象 三、参考资料总结 前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、UART 1.串口初始化 …...

STM32移植RT-Thread---时钟管理

一RTT时钟节拍概念 RT-Thread的时钟节拍&#xff08;Tick&#xff09;是操作系统用于管理时间和任务调度的一个基本单位。它在实时操作系统中尤为关键&#xff0c;用于实现任务的延时、超时管理等功能。以下是关于RT-Thread时钟节拍的简单说明&#xff1a; 1.Tick定义&#x…...

Jasypt 实现 yml 配置加密

文章目录 前言一、集成 Jasypt1. pom 依赖2. yml 依赖 3. 加密工具类3. 使用二、常见问题1. application.yml 失效问题2. 配置热更新失败问题 前言 jasypt 官方地址&#xff1a;https://github.com/ulisesbocchio/jasypt-spring-boot Jasypt可以为Springboot加密的信息很多&a…...

uniapp—android原生插件开发(2原生插件开发)

本篇文章从实战角度出发&#xff0c;将UniApp集成新大陆PDA设备RFID的全过程分为四部曲&#xff0c;涵盖环境搭建、插件开发、AAR打包、项目引入和功能调试。通过这份教程&#xff0c;轻松应对安卓原生插件开发与打包需求&#xff01; ***环境问题移步至&#xff1a;uniapp—an…...

NLP之ASR之moonshine:moonshine的简介、安装和使用方法、案例应用之详细攻略

NLP之ASR之moonshine&#xff1a;moonshine的简介、安装和使用方法、案例应用之详细攻略 目录 moonshine的简介 moonshine的安装和使用方法 1、安装 推荐使用uv管理Python环境 安装Moonshine包 Torch后端 TensorFlow后端 JAX后端 ONNX运行时 2、使用方法 0、测试 1…...

albert模型实现微信公众号虚假新闻分类

项目源码获取方式见文章末尾&#xff01; 600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 《------往期经典推荐------》 项目名称 1.【基于CNN-RNN的影像报告生成】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【GAN模型实现二次元头像生成】 4.【CNN模型实现…...

OceanBase 应用实践:如何处理数据空洞,降低存储空间

问题描述 某保险行业客户的核心系统&#xff0c;从Oracle 迁移到OceanBase之后&#xff0c;发现数据存储空间出现膨胀问题&#xff0c;数据空间 datasize9857715.48M&#xff0c;实际存储占用空间17790702.00M。根据 required_mb - data_mb 值判断&#xff0c;数据空洞较为严重…...

计算机的错误计算(一百四十八)

摘要 本节探讨 MATLAB 中 附近数的正割函数与 附近数的余割函数的计算精度问题。 例1. 已知 计算 直接贴图吧&#xff1a; 另外&#xff0c;16位的正确值分别为 0.4105556037464873e9、0.3670813182326778e13、-0.2549029285657875e8 与 -0.1248777628817462e12&am…...

MySQL记录锁、间隙锁、临键锁(Next-Key Locks)详解

行级锁&#xff0c;每次操作锁住对应的行数据。锁定粒度最小&#xff0c;发生锁冲突的概率最低&#xff0c;并发度最高。 应用在InnoDB存储引擎中。InnoDB的数据是基于索引组织的&#xff0c;行锁是通过对索引上的索引项加锁来实现的&#xff0c;而不是对记录加的锁。 对于行…...

SLM401A系列42V商业照明线性恒流芯片 线性照明调光在LED模组及灯带智能球泡灯上应用

SLM401A系列型号选型&#xff1a; SLM401A10ED-7G:QFN1010-4 SLM401A15aa-7G:SOT23-3 SLM401A20aa-7G:SOT23-3 SLM401A20ED-7G:QFN1010-4 SLM401A25aa-7G:SOT23-3 SLM401A30aa-7G:SOT23-3 SLM401A40aa-7G:SOT23-3 SLM401A50aa-7G:SOT23-3 SLM401A6…...

京东零售推荐系统可解释能力详解

作者&#xff1a;智能平台 张颖 本文导读 本文将介绍可解释能力在京东零售推荐系统中的应用实践。主要内容包括以下几大部分&#xff1a;推荐系统可解释定义、系统架构、排序可解释、模型可解释、流量可解释。 推荐系统可解释定义 推荐系统可解释的核心包括三部分&#xff0…...

蓝桥杯 懒洋洋字符串--字符串读入

题目 代码 #include <iostream>using namespace std;int main(){int n;cin>>n;char s[210][4];int ans0;for(int i0;i<n;i){scanf("%s",s[i]);}for(int i0;i<n;i){char as[i][0];char bs[i][1];char cs[i][2];// cout<<a<< <<b…...

SDL打开YUV视频

文章目录 问题1&#xff1a;如何控制帧率&#xff1f;问题2&#xff1a;如何触发退出事件&#xff1f;问题3&#xff1a;如何实时调整视频窗口的大小问题4&#xff1a;YUV如何一次读取一帧的数据&#xff1f; 问题1&#xff1a;如何控制帧率&#xff1f; 单独用一个子线程给主线…...

微服务架构面试内容整理-Archaius

Archaius 是由 Netflix 开发的一个配置管理库,主要用于处理动态配置和环境配置。在微服务架构中,Archaius 允许开发者以灵活的方式管理配置,从而更好地应对变化的需求。以下是 Archaius 的主要特点、工作原理和使用场景: 主要特点 1. 动态配置: Archaius 支持动态更新配置…...

实现 Nuxt3 预览PDF文件

安装必要的库&#xff0c;这里使用PDF.js库 npm install pdfjs-dist --save 为了解决跨域问题&#xff0c;在server/api 下 创建一个请求api&#xff0c; downloadFileByProxy.ts import { defineEventHandler } from h3;export default defineEventHandler(async event >…...

udp为什么会比tcp 有更低的延迟

UDP&#xff08;User Datagram Protocol&#xff0c;用户数据报协议&#xff09;相比TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;具有更低的延迟&#xff0c;这主要归因于UDP协议的设计特点和机制。以下是对UDP比TCP延迟低的原因的详细…...

基于java+SpringBoot+Vue的洗衣店订单管理系统设计与实现

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven mysql5.7或8.0等等组成&#x…...

HarmonyOS-消息推送

一. 服务简述 Push Kit&#xff08;推送服务&#xff09;是华为提供的消息推送平台&#xff0c;建立了从云端到终端的消息推送通道。所有HarmonyOS 应用可通过集成 Push Kit&#xff0c;实现向应用实时推送消息&#xff0c;使消息易见&#xff0c;构筑良好的用户关系&#xff0…...

数据分析:宏基因组DESeq2差异分析筛选差异物种

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍原理:计算步骤:结果:加载R包准备画图主题数据链接导入数据Differential abundance (No BP vs 2BP TA)构建`countData`矩阵过滤低丰度物种构建DESeq数据对象DESeq2差异分析画图Di…...

出海企业如何借助云计算平台实现多区域部署?

云计算de小白 如需进一步了解&#xff0c;请单击链接了解有关 Akamai 云计算的更多信息 在本文中我们将告诉大家如何在Linode云计算平台上借助VLAN快速实现多地域部署。 首先我们需要明确一些基本概念和思想&#xff1a; 部署多区域 VLAN 为了在多区域部署中在不同的 VLAN …...

硬件---1电路设计安全要点以及欧姆定律

前言&#xff1a; 一直搞的东西都偏软件&#xff0c;硬件也一直在学&#xff0c;元器件、基础电路知识、PCB设计、模电运放都学的马马虎虎&#xff0c;因此决定进行系统性学习&#xff0c;内容基本来源于手里的视频和书本以及自己的感悟。 一电路安全 1电路安全 在初期基础…...

Linux如何更优质调节系统性能

一、硬件优化 增加物理内存&#xff1a;最直接的提升系统性能的方法。内存不足时&#xff0c;系统会频繁进行交换&#xff08;swapping&#xff09;活动&#xff0c;这会显著降低系统的响应速度&#xff0c;因为磁盘IO速度远低于内存访问速度。通过增加内存&#xff0c;可以减…...

第三十五章 Vue路由进阶之声明式导航(跳转传参)

目录 一、引言 二、查询参数传参 2.1. 使用方式 2.2. 完整代码 2.2.1. main.js 2.2.2. App.vue 2.2.3. Search.vue 2.2.4. Home.vue 2.2.5. index.js 三、动态路由传参 3.1. 使用方式 3.2. 完整代码 3.2.1. main.js 3.2.2. App.vue 3.2.3. Search.vue 3.2.4. Hom…...

python爬虫自动库DrissionPage保存网页快照mhtml/pdf/全局截图/打印机另存pdf

目录 零一、保存网页快照的三种方法二、利用打印机保存pdf的方法 零 最近星球有人问如何使用页面打印功能&#xff0c;另存为pdf 一、保存网页快照的三种方法 解决方案已经放在星球内&#xff1a;https://articles.zsxq.com/id_55mr53xahr9a.html当然也可以看如下代码&…...

基于毫米波雷达和TinyML的车内检测、定位与分类

英文标题&#xff1a;In-Cabin Detection, Localization and Classification based on mmWave Radar with TinyML 作者信息&#xff1a; 王志飞&#xff0c;程一格&#xff0c;彭辉&#xff0c;周会强&#xff0c;王铮&#xff0c;刘宏全所属机构&#xff1a;Calterah Semico…...

小E的射击训练

问题描述 小E正在训练场进行射击练习&#xff0c;靶有10个环&#xff0c;靶心位于坐标(0, 0)。每个环对应不同的得分&#xff0c;靶心内&#xff08;半径为1&#xff09;得10分&#xff0c;依次向外的每个环分数减少1分。若射击点在某个半径为i的圆内&#xff0c;则得11-i分。…...

React的概念以及发展前景如何?

React是一个由Facebook开发的用于构建用户界面的的开源JavaScript库&#xff0c;它主要用于构建大型、动态的Web应用程序。React的主要特点是使用VirtualDOM&#xff08;虚拟DOM&#xff09;来优化性能&#xff0c;并使用声明式的编程方式来编写UI。 React的主要概念包括&#…...

PDF生成:全面解析,C# 如何使用iTextSharp库(或其他类似库)生成PDF文档,包括如何将位图图像嵌入PDF中。

一、概述 PDF&#xff08;Portable Document Format&#xff09;是一种广泛使用的文档格式&#xff0c;由Adobe公司在1993年推出。PDF的目标是能够在任何设备上呈现固定格式的文档&#xff0c;无论是在不同的操作系统、硬件设备&#xff0c;还是在打印时&#xff0c;都能保证文…...

如何选择最适合的消息队列?详解 Kafka、RocketMQ、RabbitMQ 的使用场景

引言 在日常开发中&#xff0c;消息队列已经成为业务场景中几乎不可或缺的一部分。无论是订单系统、日志收集、分布式事务&#xff0c;还是大数据实时流处理&#xff0c;消息队列都在支撑着这些关键环节。目前市面上常用的消息队列有三种(ActiveMQ 虽然在企业集成中仍有应用&a…...