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

数据结构与算法面试专题——桶排序

引入

桶排序,顾名思义,会用到“桶”,核心思想是将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序。桶内排完序之后,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的了。

桶排序与之前提到的排序的本质区别,就是它是不基于比较的排序。 桶排序是一种基于分治思想的排序算法,它将数据分布到有限数量的桶中,每个桶再分别排序,最后按顺序合并。

实现原理

  1. 确定桶的数量和范围:根据数据的取值范围和数据量,计算需要的桶的数量。每个桶代表一个特定的区间范围。

  2. 分配数据到桶:遍历待排序的数据,根据每个数据的值将其分配到相应的桶中。

  3. 对每个桶进行排序:可以使用其他排序算法(如插入排序、快速排序等)对每个桶中的数据进行排序。

  4. 收集结果:按照桶的顺序,将所有桶中的数据依次取出,合并到一个数组中,得到最终的有序结果。

优点

  • 高效:对于数据分布均匀的情况,桶排序的时间复杂度接近 O(n)。

  • 稳定:桶排序是稳定的排序算法,能保持相等元素的原始相对顺序。

桶排序的时间复杂度为什么是 O(n) 呢?
如果要排序的数据有 n 个,我们把它们均匀地划分到 m 个桶内,每个桶里就有 k=n/m 个元素。每个桶内部使用快速排序,时间复杂度为 O(k * logk)。m 个桶排序的时间复杂度就是 O(m * k * logk),因为 k=n/m,所以整个桶排序的时间复杂度就是 O(n*log(n/m))。
当桶的个数 m 接近数据个数 n 时,log(n/m) 就是一个非常小的常量,这个时候桶排序的时间复杂度接近 O(n)。

缺点

  • 数据分布要求高:如果数据分布不均匀,可能导致部分桶过载,影响整体效率。

  • 空间消耗大:需要额外的空间存储桶,对于大规模数据,空间复杂度可能较高。

应用场景

  • 数据分布均匀且范围明确:当待排序数据分布均匀且范围已知时,桶排序能充分利用数据特性,实现高效排序。

  • 高效内部排序算法可用:若桶内排序可选用计数排序、基数排序等线性时间复杂度的排序算法,桶排序的整体效率将显著提升。

  • 对稳定性有要求:在需要保持相等元素原始相对顺序的场景中,桶排序的稳定性使其成为理想选择。

桶排序看起来很优秀,那它是不是可以替代我们之前讲的排序算法呢?
答案当然是否定的。实际上,桶排序对要排序数据的要求是非常苛刻的。

首先,要排序的数据需要很容易就能划分成 m 个桶,并且,桶与桶之间有着天然的大小顺序。这样每个桶内的数据都排序完之后,桶与桶之间的数据不需要再进行排序。
其次,数据在各个桶之间的分布是比较均匀的。如果数据经过桶的划分之后,有些桶里的数据非常多,有些非常少,很不平均,那桶内数据排序的时间复杂度就不是常量级了。在极端情况下,如果数据都被划分到一个桶里,那就退化为 O(nlogn) 的排序算法了。
桶排序比较适合用在外部排序中。所谓的外部排序就是数据存储在外部磁盘中,数据量比较大,内存有限,无法将数据全部加载到内存中。

下面我们重点看桶排序思想下的排序:计数排序 & 基数排序

注意:

  1. 桶排序思想下的排序都是不基于比较的排序
  2. 时间复杂度为O(N),额外空间负载度O(M)
  3. 应用范围有限,需要样本的数据状况满足桶的划分

计数排序(Counting Sort)

计数排序计数排序其实是桶排序的一种特殊情况,适用于整数或有限范围内的数据排序。当要排序的 n 个数据,所处的范围并不大的时候,比如最大值是 k,我们就可以把数据划分成 k 个桶。每个桶内的数据值都是相同的,省掉了桶内排序的时间。

计数排序只能用在数据范围不大的场景中,如果数据范围k比要排序的数据n大很多,就不适合用计数排序了。而且,计数排序只能给非负整数排序,如果要排序的数据是其他类型的,要将其在不改变相对大小的情况下,转化为非负整数。

代码示例

下面是一个简单的计数排序案例

public void countSort(int[] arr) {// 如果数组为空或长度小于2,无需排序,直接返回if (arr == null || arr.length < 2) {return;}// 找出数组中的最大值,用于确定计数桶的大小int max = Integer.MIN_VALUE;for (int i = 0; i < arr.length; i++) {max = Math.max(max, arr[i]);}// 创建计数桶,索引代表数字,值代表该数字在数组中的出现次数int[] bucket = new int[max + 1];// 遍历数组,统计每个数字的出现次数for (int i = 0; i < arr.length; i++) {bucket[arr[i]]++; // 例如,如果数组中有数字5,那么bucket[5]会增加1}// 根据计数桶中的计数,将数字按顺序重新放回原数组int i = 0; // 用于跟踪原数组中的当前位置for (int j = 0; j < bucket.length; j++) {// 当前数字为j,出现次数为bucket[j]while (bucket[j]-- > 0) {arr[i++] = j; // 将数字j放入原数组的正确位置}}
}

 当然,我们还可以如下实现:

// 计数排序,适用于非负整数数组
public void countingSort(int[] a, int n) {if (n <= 1) return; // 如果数组长度小于等于1,无需排序// 查找数组中数据的范围int max = a[0];for (int i = 1; i < n; ++i) {if (max < a[i]) { // 找到数组中的最大值max = a[i];}}// 申请一个计数数组 c,下标大小从 0 到 maxint[] c = new int[max + 1];for (int i = 0; i <= max; ++i) {c[i] = 0; // 初始化计数数组为0}// 计算每个元素的个数,放入计数数组 c 中for (int i = 0; i < n; ++i) {c[a[i]]++; // 统计每个元素出现的次数}// 依次累加,得到当前元素排序后的位置for (int i = 1; i <= max; ++i) {c[i] = c[i-1] + c[i]; // 累加,得到每个元素的累计个数}// 创建临时数组 r,存储排序之后的结果int[] r = new int[n];// 根据计数数组计算排序的关键步骤for (int i = n - 1; i >= 0; --i) { // 逆序遍历原数组int index = c[a[i]] - 1; // 获取当前元素在临时数组中的位置r[index] = a[i]; // 将元素放入临时数组c[a[i]]--; // 减少计数,确保相同元素的位置正确}// 将临时数组的结果拷贝回原数组for (int i = 0; i < n; ++i) {a[i] = r[i]; // 更新原数组为排序后的结果}
}

假设输入数组为 [3, 6, 4, 1, 3, 4, 1, 4],经过计数排序后:

  • 最大值为6,计数桶大小为7(索引0-6)。

  • 计数桶统计结果为:

    • bucket[0] = 0

    • bucket[1] = 2

    • bucket[2] = 0

    • bucket[3] = 2

    • bucket[4] = 3

    • bucket[5] = 0

    • bucket[6] = 1

  • 重建数组时,按顺序将数字放入原数组:

    • 1出现2次,写入索引0和1。

    • 3出现2次,写入索引2和3。

    • 4出现3次,写入索引4、5和6。

    • 6出现1次,写入索引7。

  • 排序后的数组为 [1, 1, 3, 3, 4, 4, 4, 6]

实现原理

  1. 确定数据范围:找到待排序数据的最大值和最小值,确定数据的范围。

  2. 创建计数数组:创建一个计数数组,用于统计每个数据出现的次数。

  3. 统计次数:遍历待排序数据,统计每个数据在计数数组中的出现次数。

  4. 重建排序结果:根据计数数组中的次数,从大到小或从小到大重建排序结果。

优点

线性时间复杂度:计数排序的时间复杂度为 O(n + k),其中 n 是数据量,k 是数据的范围。

稳定:计数排序是稳定的排序算法,能保持相等元素的原始相对顺序。

缺点

适用范围有限:计数排序只适用于整数或有限范围内的数据,不适用于负数和浮点数。

空间复杂度高:需要额外的空间存储计数数组,当数据范围较大时,空间复杂度较高。

应用场景

整数排序:适用于整数数据的排序,尤其是数据范围较小的情况。

字符串排序:可以用于固定长度的字符串排序,通过字符位来排序。

基数排序(Radix Sort)

基数排序是一种非比较型整数排序算法,它按照数字的每一位进行比较和交换。基数排序的核心思想是将待排序数组分成若干个段,然后对每个段进行局部排序。

基数排序对要排序的数据是有要求的,需要可以分割出独立的“位”来比较,而且位之间有递进的关系,如果 a 数据的高位比 b 数据大,那剩下的低位就不用比较了。除此之外,每一位的数据范围不能太大,要可以用线性排序算法来排序,否则,基数排序的时间复杂度就无法做到 O(n) 了。

代码示例

// 该基数排序方法仅适用于非负数
public void radixSort(int[] arr) {// 如果数组为空或长度小于2,无需排序,直接返回if (arr == null || arr.length < 2) {return;}// 调用基数排序的辅助方法,传入起始索引、结束索引和最大位数radixSort(arr, 0, arr.length - 1, maxbits(arr));
}// 计算数组中最大值的位数
public int maxbits(int[] arr) {int max = Integer.MIN_VALUE; // 初始化最大值for (int i = 0; i < arr.length; i++) {max = Math.max(max, arr[i]); // 找出数组中的最大值}int res = 0; // 记录最大值的位数while (max != 0) { // 通过不断除以10来统计位数res++;max /= 10;}return res; // 返回最大值的位数
}// 对数组的子数组进行基数排序
// arr: 待排序数组
// L: 子数组的起始索引
// R: 子数组的结束索引
// digit: 数组中最大值的位数
public void radixSort(int[] arr, int L, int R, int digit) {final int radix = 10; // 基数为10,因为使用十进制int i = 0, j = 0;// 辅助数组,用于存储排序过程中的中间结果int[] help = new int[R - L + 1];// 根据数字的位数循环处理每一位for (int d = 1; d <= digit; d++) { // d 表示当前处理的是第几位(从低位到高位)int[] count = new int[radix]; // 计数数组,统计当前位各个数字的出现次数// 统计当前位各个数字的出现次数for (i = L; i <= R; i++) {j = getDigit(arr[i], d); // 获取当前数字的第d位数字count[j]++;}// 计算计数数组的前缀和,用于确定每个数字的位置for (i = 1; i < radix; i++) {count[i] = count[i] + count[i - 1];}// 将数字按照当前位的大小放入辅助数组for (i = R; i >= L; i--) { // 从右到左遍历原数组,保证排序的稳定性j = getDigit(arr[i], d); // 获取当前数字的第d位数字help[count[j] - 1] = arr[i]; // 将数字放入辅助数组的正确位置count[j]--; // 减少计数,确保相同位的数字顺序正确}// 将辅助数组中的有序数字复制回原数组for (i = L, j = 0; i <= R; i++, j++) {arr[i] = help[j];}}
}// 获取数字x的第d位上的数字
public int getDigit(int x, int d) {return ((x / ((int) Math.pow(10, d - 1))) % 10); // 例如,x=123,d=2,返回 2
}

实现原理

  1. 确定最大位数:找到待排序数据中的最大值,确定最大位数。

  2. 按位排序:从最低位开始,按照每一位对数组进行排序。可以使用计数排序或桶排序作为子排序算法。

  3. 合并结果:按照位数顺序,逐步合并排序结果,最终得到完全排序的数组。

优点

线性时间复杂度:基数排序的时间复杂度为 O(d * (n + k)),其中 d 是数字中的最大位数,n 是数组的长度,k 是每一位的取值范围。

稳定:基数排序是稳定的排序算法,前一位的排序结果在下一位排序中不会改变。

缺点

空间复杂度高:需要额外的空间来存储中间结果,空间复杂度较高,尤其在处理大数据时。

适用范围有限:基数排序适合数值或字符串等可以按位操作的数据,不适合复杂数据类型。

应用场景

大规模整数排序:适用于大规模的整数数据排序,尤其是位数不多且取值范围有限的情况。

字符串排序:可以用于固定长度的字符串排序,通过字符位来排序。

图像处理中的颜色排序:基数排序可用于按色彩通道分量进行排序。

排序算法总结

时间复杂度

额外空间复杂度

稳定性

选择排序

O(N^2)

O(1)

冒泡排序

O(N^2)

O(1)

插入排序

O(N^2)

O(1)

归并排序

O(N* logN)

O(N)

随机快排

O(N* logN)

O(logN)

堆排序

O(N* logN)

O(1)

计数排序

O(N)

O(M)

基数排序

O(N)

O(N)

  1. 不基于比较的排序,对样本数据有严格要求,不易改写
  2. 基于比较的排序,只要规定好两个样本怎么比大小就可以直接复用
  3. 基于比较的排序,时间复杂度的极限是O(N*logN)
  4. 时间复杂度O(N*logN)、额外空间复杂度低于O(N)、且稳定的基于比较的排序是不存在的。
  5. 为了绝对的速度选快排、为了省空间选堆排、为了稳定性选归并

相关文章:

数据结构与算法面试专题——桶排序

引入 桶排序&#xff0c;顾名思义&#xff0c;会用到“桶”&#xff0c;核心思想是将要排序的数据分到几个有序的桶里&#xff0c;每个桶里的数据再单独进行排序。桶内排完序之后&#xff0c;再把每个桶里的数据按照顺序依次取出&#xff0c;组成的序列就是有序的了。 桶排序…...

深度学习奠基作 AlexNet 论文阅读笔记(2025.2.25)

文章目录 训练数据集数据预处理神经网络模型模型训练正则化技术模型性能其他补充 训练数据集 模型主要使用2010年和2012年的 ImageNet 大规模视觉识别挑战赛&#xff08;ILSVRC&#xff09;提供的 ImageNet 的子集进行训练&#xff0c;这些子集包含120万张图像。最终&#xff…...

MongoDB 数据库简介

MongoDB 数据库简介 引言 随着互联网技术的飞速发展,数据已经成为企业的重要资产。为了高效地管理和处理这些数据,数据库技术应运而生。MongoDB作为一种流行的NoSQL数据库,因其灵活的数据模型和高效的数据处理能力,受到了广泛的关注。本文将为您详细介绍MongoDB的基本概念…...

Transformer LLaMA

一、Transformer Transformer&#xff1a;一种基于自注意力机制的神经网络结构&#xff0c;通过并行计算和多层特征抽取&#xff0c;有效解决了长序列依赖问题&#xff0c;实现了在自然语言处理等领域的突破。 Transformer 架构摆脱了RNNs&#xff0c;完全依靠 Attention的优…...

【DeepSeek开源:会带来多大的影响】

DeepSeek 开源&#xff0c;震撼登场对云计算行业的冲击 巨头云厂商的新机遇 DeepSeek 开源后&#xff0c;为云计算行业带来了巨大的变革&#xff0c;尤其是为巨头云厂商创造了新的发展机遇。以阿里云为例&#xff0c;它作为云计算行业的领军者&#xff0c;与 DeepSeek 的合作…...

Redis7——基础篇(七)

前言&#xff1a;此篇文章系本人学习过程中记录下来的笔记&#xff0c;里面难免会有不少欠缺的地方&#xff0c;诚心期待大家多多给予指教。 基础篇&#xff1a; Redis&#xff08;一&#xff09;Redis&#xff08;二&#xff09;Redis&#xff08;三&#xff09;Redis&#x…...

边缘计算:通俗易懂的全方位解析

1. 什么是边缘计算&#xff1f; 边缘计算&#xff08;Edge Computing&#xff09;是一种数据处理方式&#xff0c;它将计算任务从云端或数据中心下放到更靠近数据源&#xff08;边缘&#xff09;的设备上。 通俗理解&#xff1a; 想象你住在一个偏远的村庄&#xff0c;而最近…...

Flink 中的滚动策略(Rolling Policy)

在 Apache Flink 中&#xff0c;滚动策略&#xff08;Rolling Policy&#xff09;是针对日志&#xff08;或数据流&#xff09;文件输出的一种管理策略&#xff0c;它决定了在日志文件的大小、时间或其他条件满足特定标准时&#xff0c;如何“滚动”生成新的日志文件。滚动策略…...

GPU和FPGA的区别

GPU&#xff08;Graphics Processing Unit&#xff0c;图形处理器&#xff09;和 FPGA&#xff08;Field-Programmable Gate Array&#xff0c;现场可编程门阵列&#xff09;不是同一种硬件。 我的理解是&#xff0c;虽然都可以用于并行计算&#xff0c;但是GPU是纯计算的硬件…...

网易云音乐分布式KV存储实践与演进

随着网易云音乐业务的快速发展&#xff0c;推荐和搜索场景对分布式KV存储的需求日益增长。本文将深入探讨网易云音乐在分布式KV存储方面的实践和演进&#xff0c;分析其技术选型、架构设计以及未来发展方向。 一、业务背景 网易云音乐的业务场景对分布式KV存储提出了高并发、…...

WordPress平台如何接入Deepseek,有效提升网站流量

深夜改代码到崩溃&#xff1f;《2024全球CMS生态报告》揭露&#xff1a;78%的WordPress站长因API对接复杂&#xff0c;错失AI内容红利。本文实测「零代码接入Deepseek」的保姆级方案&#xff0c;配合147SEO的智能发布系统&#xff0c;让你用3个步骤实现日均50篇EEAT合规内容自动…...

【嵌入式】STM32内部NOR Flash磨损平衡与掉电保护总结

1. NOR Flash与NAND Flash 先deepseek看结论&#xff1a; 特性Nor FlashNAND Flash读取速度快&#xff08;支持随机访问&#xff0c;直接执行代码&#xff09;较慢&#xff08;需按页顺序读取&#xff09;写入/擦除速度慢&#xff08;擦除需5秒&#xff0c;写入需逐字节操作&…...

什么是磁盘阵列(RAID)?如何提高磁盘阵列的性能

什么是磁盘阵列 ‌磁盘阵列&#xff08;RAID&#xff09;是一种将多个独立的硬盘组合成一个逻辑存储单元的技术&#xff0c;旨在提高数据存储的性能、容量、可靠性和冗余性‌。‌磁盘阵列通过将数据分割成多个区段并分别存储在不同的硬盘上&#xff0c;利用个别磁盘提供数据加…...

轻量级日志管理平台Grafana Loki

文章目录 轻量级日志管理平台Grafana Loki背景什么是Loki为什么使用 Grafana Loki&#xff1f;架构Log Storage Grafana部署使用基于 Docker Compose 安装 LokiMinIO K8s集群部署Loki采集Helm 部署方式和案例 参考 轻量级日志管理平台Grafana Loki 背景 在微服务以及云原生时…...

k8s集群部署

集群结构 角色IPmaster192.168.35.135node1192.168.35.136node2192.168.35.137 部署 #需在三台主机上操作 //关闭防火墙 [rootmaster ~]# systemctl disable --now firewalld//关闭selinux [rootmaster ~]# sed -i s/enforcing/disabled/ /etc/selinux/config//关闭swap分区…...

STM32MP157A-FSMP1A单片机移植Linux系统SPI总线驱动

SPI总线驱动整体上与I2C总线驱动类型&#xff0c;差别主要在设备树和数据传输上&#xff0c;由于SPI是由4根线实现主从机的通信&#xff0c;在设备树上配置时需要对SPI进行设置。 原理图可知&#xff0c;数码管使用的SPI4对应了单片机上的PE11-->SPI4-NSS,PE12-->SPI4-S…...

系统基础与管理(2025更新中)

‌一、Linux 核心架构与组件‌ ‌内核架构‌ ‌核心职责‌&#xff1a; 管理进程生命周期、内存分配、硬件驱动交互及文件系统操作。 模块化设计支持动态加载硬件驱动&#xff08;如modprobe加载内核模块&#xff09;&#xff0c;提升灵活性和扩展性。 ‌内存管理‌&#xff1a…...

Python--内置函数与推导式(下)

3. 内置函数 数学运算类 函数说明示例​abs​绝对值​abs(-10) → 10​​pow​幂运算​pow(2, 3) → 8​​sum​求和​sum([1,2,3]) → 6​​divmod​返回商和余数​divmod(10, 3) → (3, 1)​ 数据转换类 # 进制转换 print(bin(10)) # 0b1010 print(hex(255)) # 0x…...

可狱可囚的爬虫系列课程 14:10 秒钟编写一个 requests 爬虫

一、前言 当重复性的工作频繁发生时&#xff0c;各种奇奇怪怪提高效率的想法就开始萌芽了。当重复代码的模块化封装已经不能满足要求的时候&#xff0c;更高效的方式就被揭开了神秘的面纱。本文基于这样的想法&#xff0c;来和大家探讨如何 10 秒钟编写一个 requests 爬虫程序。…...

Windows golang安装和环境配置

【1】、golang 1.19 sdk下载 https://download.csdn.net/download/notfindjob/90422529 【2】、安装 【3】、配置 GOPATH目录 【4】、LiteIDE下载安装 https://download.csdn.net/download/notfindjob/90422580 【5】、打开LiteIDE&#xff0c;选择查看->管理GOPATH&…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

工程地质软件市场:发展现状、趋势与策略建议

一、引言 在工程建设领域&#xff0c;准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具&#xff0c;正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...