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

详解七大排序

目录

一.直接插入排序

(1)基本思想

(2)算法步骤

(3)代码实现

(4)算法特性

(5)算法优化

(6)示例演示

二.希尔排序

(1)基本思想

(2)算法步骤

(3)代码实现

(4)算法特性

 (5)算法优化

(6)实例演示

 三.选择排序

(1)基本思想

(2)算法步骤

(3)代码实现

1)每次交换一个元素(最小元素):

2)每次交换两个元素(一个最大元素,一个最小元素):

(4)算法特性

(5)算法优化

(6)实例演示

 四.堆排序

(1)基本思想

(2)算法步骤

(3)代码实现

 (4)算法特性

(5)算法优化

(6)实例演示

1)建堆过程:

 2)排序阶段

五.冒泡排序

(1)基本思想

(2)算法步骤

(3)代码实现

(4)算法特性

 (5)算法优化

1. 提前终止(已实现)

2. 鸡尾酒排序(双向冒泡)

3. 记录最后交换位置

 (6)实例演示

六.快速排序

(1)基本思想

(2)算法步骤

(3)代码实现

 (4)算法特性

1. 高效的平均性能

2. 内存效率高

3. 实现灵活

最坏情况性能差

2. 不稳定排序

3. 递归实现的风险

(5)算法优化

1. 三数取中法选择基准值

2. 尾递归优化

3. 插入排序混合优化

 (6)实例演示

示例1:基础快速排序流程

示例2:三数取中法优化

示例3:三路快排处理重复元素

七.归并排序

(1)基本思想

(2)算法步骤

(3)代码实现

(4)算法特性

(5)算法优化

1. 小规模数据使用插入排序

2. 减少不必要的数组复制

3. 提前终止合并过程

(6)实例演示

分解阶段

合并阶段

八.常见排序表

一.直接插入排序

(1)基本思想

直接插入排序是一种简单的排序算法,其基本思想是:将待排序的元素逐个插入到已排序序列的适当位置,直到所有元素都插入完毕。

(2)算法步骤

  1. 将第一个元素视为已排序序列

  2. 取出下一个元素,在已排序序列中从后向前扫描

  3. 如果已排序元素大于新元素,将该元素移到下一位置

  4. 重复步骤3,直到找到已排序元素小于或等于新元素的位置

  5. 将新元素插入到该位置

  6. 重复步骤2~5,直到所有元素都排序完成

(3)代码实现

 for (int i = 1; i < array.length; i++) {int temp = array[i];int j = i - 1;for (; j >= 0; j--) {if (array[j] > temp) {/*array[j+1]就是temp,但是1个i对应很多个j,所以i不是等于j+1;要用j+1来表示temp*/array[j + 1] = array[j];} else {//不用改变元素位置,把取出来的temp再放回去// array[j+1]=temp;break;}}/*出了j的循环,证明j这时已经小于0了,也就是-1把取出来的temp再放回去j+1的位置,也就是下标0*/array[j + 1] = temp;}

(4)算法特性

特性说明
时间复杂度最好O(n),最坏和平均O(n²)
空间复杂度O(1)(原地排序)
稳定性稳定排序
适用场景小规模数据或基本有序数据
优点

(1)实现简单直观;

(2)稳定排序,不会改变相同元素的相对顺序;

(3)原地排序,不需要额外的存储空间(空间复杂度O(1));

(4)对小规模或部分有序数据高效;

缺点

(1)时间复杂度高:平均O(n²);

(2)对逆序数据表现最差;

(3)数据移动频繁;

(5)算法优化

  1. 二分查找优化:在已排序部分使用二分查找确定插入位置(减少比较次数)

  2. 希尔排序:是插入排序的改进版,通过分组插入提高效率

(6)示例演示

以数组 [5, 2, 4, 6, 1, 3] 为例:

初始:  [5, 2, 4, 6, 1, 3]
第1轮: [2, 5, 4, 6, 1, 3] (插入2)
第2轮: [2, 4, 5, 6, 1, 3] (插入4)
第3轮: [2, 4, 5, 6, 1, 3] (插入6)
第4轮: [1, 2, 4, 5, 6, 3] (插入1)
第5轮: [1, 2, 3, 4, 5, 6] (插入3)

直接插入排序虽然简单,但在处理小规模或部分有序数据时效率较高,且实现简单,常被用作其他高级排序算法的子过程。

二.希尔排序

(1)基本思想

希尔排序(Shell Sort)是插入排序的一种高效改进版本,由Donald Shell于1959年提出。其核心思想是通过分组插入排序逐步减少间隔(增量),最终实现整个数组的有序化。

希尔排序的思想是通过分组来减少增量,提前进行多次小范围排序,使得数据有序化,最后当gap=1(最后一次排序)时,实现高效率的直接插入排序。

(2)算法步骤

  1. 选择增量序列:确定一个递减的增量序列(例如初始增量为数组长度的一半,后续逐步减半)。

  2. 分组插入排序:对每个增量间隔形成的子序列进行直接插入排序

  3. 缩小增量:重复步骤2,直到增量为1,此时整个数组作为一整个序列进行最后一次插入排序(希尔排序的最后一次排序就是直接插入排序)。

(3)代码实现

 public static void shellSort(int[] array) {int gap = array.length / 2;
/*随着gap的递减,分的组数也越来越少,直接组数为1,gap=1,这时进行直接插入排序*/while (gap > 0) {shell(array, gap);gap = gap / 2;}}//希尔排序内排序public static void shell(int[] array, int gap) {for (int i = gap; i < array.length; i++) {int temp = array[i];int j = i - gap;for (; j >= 0; j = j - gap) {if (array[j] > temp) {array[j + gap] = array[j];} else {break;}}//此时j为负数,但不是-1array[j + gap] = temp;}}

希尔排序是优化版的直接插入排序,旨在提升直接插入排序的效率。

并且希尔排序的最后一次排序就是直接插入排序,希尔排序之所以效率高是因为希尔排序通过前面几次小范围排序使得数组逐渐有序化(直接插入排序在数据有序化的时候效率高)。

(4)算法特性

特性说明
时间复杂度最坏O(n²),平均 O(n log n)(当希尔增量(n/2递减)时)
空间复杂度O(1)
稳定性不稳定排序(相同元素可能在排序时改变相对顺序)
适用场景中小规模数据排序(特别是内存受限环境)
优点

原地排序,不需要额外的存储空间;

比直接插入排序效率更高,更适合中等规模数据;

灵活性强,可以通过选择不同增量序列来优化性能。

缺点

不稳定排序,相同元素可能在排序时改变相对顺序;

增量依赖,性能受增量序列的选择影响较大;

理论复杂,最佳增量序列至今尚无统一结论。

希尔排序的时间复杂度分析是算法理论中的一个经典难题,其复杂度高度依赖增量序列的选择目前无法精确计算所有情况下的时间复杂度。 

 (5)算法优化

Sedgewick增量优化:

使用Robert Sedgewick提出的增量序列实现的希尔排序改进版本。这种增量序列通过数学优化显著提升了希尔排序的性能,是目前已知综合表现最优的增量序列之一

public static void shellSortOptimized(int[] arr) {int n = arr.length;// Sedgewick增量序列(部分)int[] gaps = {1073, 281, 77, 23, 8, 1}; for (int gap : gaps) {if (gap >= n) continue;for (int i = gap; i < n; i++) {int temp = arr[i];int j;for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {arr[j] = arr[j - gap];}arr[j] = temp;}}
}

(6)实例演示

算法演示(以希尔增量为例)
初始数组:[12, 34, 54, 2, 3, 8, 15, 29]增量 = 4:分组:[12,3], [34,8], [54,15], [2,29]排序后:[3,8,15,2,12,34,54,29]增量 = 2:分组:[3,15,12,54], [8,2,34,29]排序后:[3,2,12,8,15,29,54,34]增量 = 1:对整个数组插入排序,最终有序。

希尔排序的时间复杂度并非固定值,而是由增量序列决定。早期资料中的 O(n log n) 是对特定场景的简化描述,现代研究更倾向于具体分析增量序列的影响。实际开发中,选择优化增量序列可显著提升性能,使其在小规模数据排序中具备竞争力。

 三.选择排序

(1)基本思想

选择排序是一种简单直观的排序算法,其核心思想是每次从未排序序列中选出最小(或最大)元素,将其放到已排序序列的末尾,直到所有元素排序完成。

(2)算法步骤

  1. 初始化:整个数组视为未排序序列

  2. 寻找最小值:遍历未排序序列,找到最小元素的位置

  3. 交换位置:将最小元素与未排序序列的第一个元素交换

  4. 缩小范围:将已找到的最小元素归入已排序序列

  5. 重复操作:重复步骤2~4,直到所有元素有序

(3)代码实现

1)每次交换一个元素(最小元素):

public static void selectSort(int[] array) {for (int i = 0; i < array.length; i++) {int minIndex = i;for (int j = i + 1; j < array.length; j++) {if (array[j] < array[minIndex]) {minIndex = j;}}//这时j已经遍历完数组,交换存储的最小值下标元素和array[i]swap(array, minIndex, i);}}//定义方法:交换数组中两个下标对应的元素public static void swap(int[] array, int i, int j) {int temp = array[i];array[i] = array[j];array[j] = temp;}

2)每次交换两个元素(一个最大元素,一个最小元素):

 public static void selectSort2(int[] array) {int left = 0;int right = array.length - 1;while (left < right) {int minIndex = left;int maxIndex = left;for (int i = left + 1; i <= right; i++) {if (array[i] > array[maxIndex]) {maxIndex = i;} else if (array[i] < array[minIndex]) {minIndex = i;}}//走完i循环,交换元素swap(array, minIndex, left);if (maxIndex == left) {swap(array, left, right);} else {swap(array, maxIndex, right);}left++;right--;}}

(4)算法特性

特性说明
时间复杂度固定 O(n²)(无论数据是否有序)
空间复杂度O(1)(原地排序)
稳定性通常不稳定(可特殊实现为稳定)
交换次数最多 n-1 次交换
比较次数固定 n(n−1)22n(n−1)​ 次
优点
  1. 简单直观:逻辑清晰,易于理解和实现

  2. 交换次数少:每轮仅需一次交换(相比冒泡排序更高效)

  3. 内存友好:原地排序,无需额外空间

  4. 适用小数据:数据量较小时实际性能尚可

缺点
  1. 效率低下:时间复杂度始终为 O(n²),不适合大规模数据

  2. 无适应性:无论数据初始状态如何,比较次数固定

  3. 不稳定排序:默认实现会改变相同元素的相对顺序

(5)算法优化

  1. 双向选择排序(鸡尾酒选择排序)

    • 同时寻找最小值和最大值,减少循环次数

    • 优化后比较次数减少约50%

  2. 堆排序优化

    • 使用堆结构优化选择过程,时间复杂度降为 O(n log n)

    • 实际上堆排序就是选择排序的高级变种

(6)实例演示

初始状态: [5, 2, 4, 6, 1, 3]
第1轮:找到最小值1 → 交换位置 → [1, 2, 4, 6, 5, 3]
第2轮:找到次小值2 → 无需交换 → [1, 2, 4, 6, 5, 3]
第3轮:找到最小值3 → 交换位置 → [1, 2, 3, 6, 5, 4]
第4轮:找到最小值4 → 交换位置 → [1, 2, 3, 4, 5, 6]
第5轮:数组已有序,排序完成

 四.堆排序

(1)基本思想

堆排序是一种基于完全二叉树结构的高效排序算法,利用堆的性质进行排序。其核心思想是:

  1. 构建最大堆(或最小堆),将无序数组转换为堆结构

  2. 逐步取出堆顶元素(最大值或最小值),与堆末尾元素交换并调整堆

  3. 重复调整直到堆大小为1,完成排序

(2)算法步骤

  1. 构建最大堆
    从最后一个非叶子节点开始,自底向上调整堆结构

  2. 堆排序阶段

    • 交换堆顶与末尾元素(最大值归位)

    • 缩小堆范围并重新调整堆

    • 重复直到堆大小为1

(3)代码实现

    public static void heapSort(int[] array) {createHeap(array);for (int end = array.length - 1; end >= 0; end--) {//交换堆顶元素和最后一个元素swap(array, 0, end);//调整剩余元素为大根堆siftDown(array, 0, end);}}//定义方法:创建1个大根堆public static void createHeap(int[] array) {//确定最后1棵子树的位置for (int parent = (array.length - 1 - 1) / 2; parent >= 0; parent--) {//siftDown是将1棵子树调整为大根堆,所以要使parent--,调整所有的子树至大根堆siftDown(array, parent, array.length);}}/*定义方法:向下调整;将1个堆调整为大根堆在数组array中向下调整到length位置*/public static void siftDown(int[] array, int parent, int length) {int child = 2 * parent + 1;while (child < length) {//拿到左右孩子最大值if (child < array.length - 1 && array[child] >= array[child + 1]) {child++;}//如果孩子最大值大于父亲节点,交换两个节点的值if (array[child] > array[parent]) {swap(array, child, parent);//继续向下调整parent = child;child = 2 * parent + 1;} else {break;}}}

 (4)算法特性

特性说明
时间复杂度O(n log n)
空间复杂度O(1)(原地排序)
稳定性不稳定
优点
  1. 时间复杂度稳定:始终保证O(n log n)的性能

  2. 内存高效:原地排序,无需额外存储空间

  3. 适合大数据:处理海量数据时不会出现快速排序的最坏情况

  4. 优先级队列基础:堆结构的重要应用场景

缺点
  1. 不稳定排序:相同值元素的相对位置可能改变

  2. 缓存不友好:跳跃式访问内存,可能影响实际性能

  3. 常数项较大:实际运行速度通常略慢于快速排序

(5)算法优化

  1. 非递归实现
    将调整方法改为迭代实现,避免递归调用开销

  2. 多叉堆优化
    使用三叉堆或四叉堆(适用于现代CPU缓存特性)

  3. 并行建堆
    对大规模数据可采用多线程并行调整子树

(6)实例演示

(以数组[12, 11, 13, 5, 6, 7]为例)

1)建堆过程:

初始数组: [12, 11, 13, 5, 6, 7]
转换为完全二叉树:12/  \11   13/ \   /5  6 7调整非叶子节点(索引2→1→0):
最终最大堆: [13, 11, 12, 5, 6, 7]
对应二叉树:13/  \11   12/ \   /5  6 7

 2)排序阶段

第1次交换:13↔7 → [7, 11, 12, 5, 6, 13]
调整堆:   [12, 11, 7, 5, 6, 13]第2次交换:12↔6 → [6, 11, 7, 5, 12, 13]
调整堆:   [11, 6, 7, 5, 12, 13]...(重复过程)...
最终结果: [5, 6, 7, 11, 12, 13]

五.冒泡排序

(1)基本思想

冒泡排序是一种简单的交换排序算法,其核心思想是通过相邻元素的比较和交换,将较大的元素逐步“冒泡”到数组末尾。每一轮遍历都会确定一个当前未排序部分的最大值。

(2)算法步骤

  1. 外层循环:控制排序轮数(共需 n-1 轮,n 为数组长度)

  2. 内层循环:遍历未排序部分,比较相邻元素

  3. 元素交换:若当前元素 > 后一个元素,则交换位置

  4. 优化判断:若某轮无交换发生,提前终止排序

(3)代码实现

public class BubbleSort {public static void bubbleSort(int[] arr) {if (arr == null || arr.length <= 1) return;int n = arr.length;boolean swapped; // 优化标志for (int i = 0; i < n - 1; i++) {swapped = false;// 每轮确定一个最大值,遍历范围逐渐缩小for (int j = 0; j < n - 1 - i; j++) {if (arr[j] > arr[j + 1]) {// 交换元素int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;swapped = true;}}// 若本轮无交换,说明已有序,提前结束if (!swapped) break;}}public static void main(String[] args) {int[] arr = {64, 34, 25, 12, 22, 11};bubbleSort(arr);System.out.println(Arrays.toString(arr)); // 输出 [11, 12, 22, 25, 34, 64]}
}

(4)算法特性

特性说明
时间复杂度最好O(n),最坏和平均O(n²)
空间复杂度O(1)(原地排序)
稳定性稳定排序
交换次数最多 n(n−1)22n(n−1)​ 次
优点
  1. 实现简单:代码逻辑直观,适合教学和小规模数据

  2. 稳定性:相等元素不会交换,保持原有顺序

  3. 空间效率:无需额外内存空间

  4. 适应性:对部分有序数据效率较高(通过优化标志)

缺点
  1. 效率低下:大规模数据排序速度显著下降

  2. 冗余比较:即便数据已有序仍需多轮遍历(未优化版本)

 (5)算法优化

1. 提前终止(已实现)

  • 通过swapped标志检测是否发生交换

  • 最佳情况时间复杂度优化至O(n)(完全有序数组)

2. 鸡尾酒排序(双向冒泡)

  • 交替进行正向和反向遍历

  • 对包含大量无序小元素的数组更高效

鸡尾酒排序代码实例: 

public static void cocktailSort(int[] arr) {int left = 0;int right = arr.length - 1;boolean swapped;while (left < right) {// 正向遍历swapped = false;for (int i = left; i < right; i++) {if (arr[i] > arr[i+1]) {swap(arr, i, i+1);swapped = true;}}if (!swapped) break;right--;// 反向遍历swapped = false;for (int i = right; i > left; i--) {if (arr[i] < arr[i-1]) {swap(arr, i, i-1);swapped = true;}}if (!swapped) break;left++;}
}

3. 记录最后交换位置

  • 记录每轮最后一次交换的位置,减少无效比较

int lastSwapIndex = 0;
int sortBorder = arr.length - 1;for (int i = 0; i < arr.length - 1; i++) {boolean swapped = false;for (int j = 0; j < sortBorder; j++) {if (arr[j] > arr[j+1]) {swap(arr, j, j+1);swapped = true;lastSwapIndex = j;}}sortBorder = lastSwapIndex;if (!swapped) break;
}

 (6)实例演示

(以数组[5, 2, 4, 6, 1, 3]为例)

初始状态: [5, 2, 4, 6, 1, 3]第1轮遍历:
2 5 4 6 1 3 → 2 4 5 6 1 3 → 2 4 5 1 6 3 → 2 4 5 1 3 6
确定最大值6归位第2轮遍历:
2 4 5 1 3 → 2 4 1 5 3 → 2 4 1 3 5
确定次大值5归位第3轮遍历:
2 4 1 3 → 2 1 4 3 → 2 1 3 4
确定4归位第4轮遍历:
2 1 3 → 1 2 3
确定3归位(优化:此时已有序,提前结束)

六.快速排序

(1)基本思想

快速排序是一种高效的分治算法,核心思想是通过基准值(pivot)划分数组,将小于基准值的元素放在左侧,大于基准值的元素放在右侧,然后递归处理左右子数组,直到整个数组有序。

(2)算法步骤

  1. 选择基准值(Pivot):从数组中选取一个元素作为基准

  2. 分区操作(Partition):重新排列数组,使小于基准值的元素在左,大于基准值的在右

  3. 递归排序:对左右两个子数组递归执行上述步骤

(3)代码实现

public class QuickSort {public static void quickSort(int[] arr) {if (arr == null || arr.length <= 1) return;sort(arr, 0, arr.length - 1);}private static void sort(int[] arr, int low, int high) {if (low < high) {int pivotIndex = partition(arr, low, high);sort(arr, low, pivotIndex - 1);  // 递归处理左子数组sort(arr, pivotIndex + 1, high); // 递归处理右子数组}}private static int partition(int[] arr, int low, int high) {int pivot = arr[high];  // 选择最后一个元素为基准值int i = low - 1;        // 指向比基准值小的最后一个元素for (int j = low; j < high; j++) {if (arr[j] <= pivot) {i++;swap(arr, i, j);}}swap(arr, i + 1, high); // 将基准值放到正确位置return i + 1;}private static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}public static void main(String[] args) {int[] arr = {10, 7, 8, 9, 1, 5};quickSort(arr);System.out.println(Arrays.toString(arr)); // 输出 [1, 5, 7, 8, 9, 10]}
}

 (4)算法特性

特性说明
平均时间复杂度O(n log n)
最坏时间复杂度O(n²)(可通过优化避免)
空间复杂度O(log n)(递归调用栈)
稳定性不稳定
最佳适用场景大规模随机数据
优点

1. 高效的平均性能

  • 时间复杂度:平均情况O(n log n),实际应用中效率通常高于其他O(n log n)算法(如归并排序)

  • 比较次数少:相比归并排序,快速排序的比较次数通常更少

2. 内存效率高

  • 原地排序:只需O(log n)的栈空间(递归调用),不需要额外存储空间

  • 缓存友好:顺序访问内存,能有效利用CPU缓存

3. 实现灵活

  • 可通过多种方式选择基准值(pivot)

  • 可优化为三路快排处理重复元素

  • 可与非递归实现结合

缺点

最坏情况性能差

  • 最坏时间复杂度:O(n²)(当输入已排序且基准选择不当时)

  • 解决方案:随机化基准选择或三数取中法

2. 不稳定排序

  • 相同元素的相对位置可能改变

  • 示例:排序[3①, 2, 3②]可能得到[2, 3②, 3①]

3. 递归实现的风险

  • 深度递归可能导致栈溢出

  • 解决方案:尾递归优化或改为迭代实现

(5)算法优化

1. 三数取中法选择基准值

避免最坏情况(已排序数组导致O(n²))

private static int selectPivot(int[] arr, int low, int high) {int mid = low + (high - low)/2;// 比较low/mid/high三个位置的元素if (arr[low] > arr[mid]) swap(arr, low, mid);if (arr[low] > arr[high]) swap(arr, low, high);if (arr[mid] > arr[high]) swap(arr, mid, high);return mid; // 返回中间值索引
}

2. 尾递归优化

减少递归调用栈深度

private static void sortOptimized(int[] arr, int low, int high) {while (low < high) {int pivotIndex = partition(arr, low, high);if (pivotIndex - low < high - pivotIndex) {sortOptimized(arr, low, pivotIndex - 1);low = pivotIndex + 1;} else {sortOptimized(arr, pivotIndex + 1, high);high = pivotIndex - 1;}}
}

3. 插入排序混合优化

对小规模子数组(如长度≤15)改用插入排序

private static final int INSERTION_THRESHOLD = 15;private static void sort(int[] arr, int low, int high) {if (high - low <= INSERTION_THRESHOLD) {insertionSort(arr, low, high);return;}// ...原快速排序逻辑
}

 (6)实例演示

示例1:基础快速排序流程

输入数组[10, 80, 30, 90, 40, 50, 70]

步骤演示

  1. 选择基准值:70(末尾元素)

  2. 分区过程:

    • 10 < 70 → 保留

    • 80 > 70 → 跳过

    • 30 < 70 → 与80交换 → [10, 30, 80, 90, 40, 50, 70]

    • 90 > 70 → 跳过

    • 40 < 70 → 与80交换 → [10, 30, 40, 90, 80, 50, 70]

    • 50 < 70 → 与90交换 → [10, 30, 40, 50, 80, 90, 70]

  3. 最终交换基准值 → [10, 30, 40, 50, 70, 90, 80]

  4. 递归处理左右子数组

示例2:三数取中法优化

输入数组[1, 2, 3, 4, 5, 6, 7](已排序数组)

优化步骤

  1. 选择low=1, mid=4, high=7

  2. 三数排序后取中值:4

  3. 分区后得到平衡划分,避免O(n²)最坏情况

示例3:三路快排处理重复元素

输入数组[3, 1, 3, 2, 3, 3, 4]

分区过程

初始:lt=0, gt=6, i=1, pivot=3
[3, 1, 3, 2, 3, 3, 4]步骤1:arr[1]=1 < 3 → 交换lt(0)和i(1)
[1, 3, 3, 2, 3, 3, 4] (lt=1, i=2)步骤2:arr[2]=3 == 3 → i++
(lt=1, i=3)步骤3:arr[3]=2 < 3 → 交换lt(1)和i(3)
[1, 2, 3, 3, 3, 3, 4] (lt=2, i=4)...最终得到:
<3的部分:[1, 2]
=3的部分:[3, 3, 3, 3]
>3的部分:[4]

七.归并排序

(1)基本思想

归并排序(Merge Sort)是一种经典的分治算法,由约翰・冯・诺伊曼在 1945 年提出。它的基本思想是将一个大问题分解为多个小问题,分别解决这些小问题,最后将小问题的解合并起来得到大问题的解。

归并排序采用分治策略,具体分为两个阶段:

  • 分解(Divide):将待排序的数组从中间分成两个子数组,然后递归地对这两个子数组继续进行分解,直到每个子数组只有一个元素(因为单个元素的数组本身就是有序的)。
  • 合并(Merge):将两个有序的子数组合并成一个有序的数组。合并过程中,比较两个子数组的元素,将较小的元素依次放入新的数组中,直到两个子数组的所有元素都被放入新数组。

(2)算法步骤

  1. 分解阶段
    • 找到数组的中间位置,将数组分成左右两部分。
    • 递归地对左右两部分进行分解,直到每个子数组只有一个元素。
  2. 合并阶段
    • 创建一个临时数组,用于存放合并后的结果。
    • 比较左右两个子数组的元素,将较小的元素依次放入临时数组中。
    • 将临时数组中的元素复制回原数组。

(3)代码实现

 private static void mergechild(int[] array, int left, int right) {if (left >= right) {return;}int mid = (left + right) / 2;mergechild(array, left, mid);mergechild(array, mid + 1, right);merge(array,left,mid,right);}
//定义方法:合并两个子数组private static void merge(int[] array, int left, int mid, int right) {int[] tempArray = new int[right - left + 1];int k = 0;//临时数组tempArray的下标int start1 = left;int start2 = mid + 1;int end1 = mid;int end2 = right;//两个子数组中都有数据while (start1 <= end1 && start2 <= end2) {if (array[start1] > array[start2]) {tempArray[k] = array[start2];k++;start2++;} else if (array[start1] <= array[start2]) {tempArray[k] = array[start1];k++;start1++;}}//1个子数组中没有数据了,跳出while循环while (start1 <= end1) {tempArray[k] = array[start1];k++;start1++;}while (start2 <= end2) {tempArray[k] = array[start2];k++;start2++;}//将tempArray中的元素复制回原数组for (int i = 0; i < tempArray.length; i++) {array[i+left]=tempArray[i];}}

(4)算法特性

特性说明
平均时间复杂度 O(nlogn)
空间复杂度 O(n)
稳定性稳定
最佳适用场景处理大规模数据
优点
  • 稳定性:归并排序是一种稳定的排序算法,即相等元素的相对顺序在排序前后不会改变。
  • 时间复杂度稳定:无论输入数据的分布如何,归并排序的时间复杂度都是 O(nlogn)。
缺点
  • 空间复杂度较高:需要额外的 O(n) 空间来存储临时数组。
  • 常数因子较大:由于需要频繁地进行数组的复制和合并操作,归并排序的常数因子相对较大,在处理小规模数据时效率不如一些简单的排序算法(如插入排序)。

(5)算法优化

1. 小规模数据使用插入排序

对于小规模的数据,插入排序的常数时间开销相对较小,性能可能比归并排序更好。因此当子数组规模较小时,可以采用插入排序来处理,减少递归调用带来的开销。

2. 减少不必要的数组复制

在归并过程中,频繁地创建和复制临时数组会带来一定的性能开销。可以通过交替使用原数组和临时数组来减少这种开销。

3. 提前终止合并过程

在合并两个有序子数组时,如果发现其中一个子数组的所有元素都已经小于另一个子数组的所有元素,就可以提前终止合并过程。

(6)实例演示

假设我们有一个待排序的数组 [38, 27, 43, 3, 9, 82, 10]

分解阶段
  • 首先将原数组从中间分成两部分:[38, 27, 43, 3] 和 [9, 82, 10]
  • 对这两个子数组继续分解,[38, 27, 43, 3] 分成 [38, 27] 和 [43, 3][9, 82, 10] 分成 [9, 82] 和 [10]
  • 继续分解,[38, 27] 分成 [38] 和 [27][43, 3] 分成 [43] 和 [3][9, 82] 分成 [9] 和 [82]。此时所有子数组都只有一个元素,分解结束。
合并阶段
  • 合并 [38] 和 [27] 得到 [27, 38];合并 [43] 和 [3] 得到 [3, 43];合并 [9] 和 [82] 得到 [9, 82]
  • 合并 [27, 38] 和 [3, 43] 得到 [3, 27, 38, 43][9, 82] 和 [10] 合并得到 [9, 10, 82]
  • 最后合并 [3, 27, 38, 43] 和 [9, 10, 82] 得到最终的有序数组 [3, 9, 10, 27, 38, 43, 82]

八.常见排序表

排序算法时间复杂度(最好)时间复杂度(平均)时间复杂度(最坏)空间复杂度稳定性备注
冒泡排序O(n)O(n²)O(n²)O(1)稳定简单但效率低
选择排序O(n²)O(n²)O(n²)O(1)不稳定交换次数最少
插入排序O(n)O(n²)O(n²)O(1)稳定对小规模数据高效
快速排序O(n log n)O(n log n)O(n²)O(log n)不稳定实际应用中最快(优化后)
归并排序O(n log n)O(n log n)O(n log n)O(n)稳定稳定且适合外部排序
堆排序O(n log n)O(n log n)O(n log n)O(1)不稳定适合大规模数据
希尔排序O(n log n)O(n log² n)O(n²)O(1)不稳定插入排序的改进版

相关文章:

详解七大排序

目录 一.直接插入排序 &#xff08;1&#xff09;基本思想 &#xff08;2&#xff09;算法步骤 &#xff08;3&#xff09;代码实现 &#xff08;4&#xff09;算法特性 &#xff08;5&#xff09;算法优化 &#xff08;6&#xff09;示例演示 二.希尔排序 &#xff08…...

第八章 Python基础进阶-数据可视化(终)

此章节练习主要分为&#xff1a;折线图、地图、柱状图&#xff0c;若用户只是学习Python的基础语法知识&#xff0c;可以不看此章节。 主要是讲解第三方包PyEcharts技术&#xff0c;Python数据的可视化操作。 一.json数据格式 json的概念&#xff1a; &#xff08;1&#x…...

【Hadoop3.1.4】完全分布式集群搭建

一、虚拟机的建立与连接 1.建立虚拟机 详情见【Linux】虚拟机的安装 把上面三个参数改掉 2.连接虚拟机 具体见【Linux】远程连接虚拟机防火墙 二、修改主机名 在Centos7中直接使用root用户执行hostnamectl命令修改&#xff0c;重启&#xff08;reboot&#xff09;后永久生…...

NLP简介及其发展历史

自然语言处理&#xff08;Natural Language Processing&#xff0c;简称NLP&#xff09;是人工智能和计算机科学领域中的一个重要分支&#xff0c;致力于实现人与计算机之间自然、高效的语言交流。本文将介绍NLP的基本概念以及其发展历史。 一、什么是自然语言处理&#xff1f…...

Java异步编程中的CompletableFuture介绍、常见错误及最佳实践

一、Future接口的局限性 Java 5引入的Future接口为异步编程提供了基础支持&#xff0c;但其设计存在明显局限性&#xff0c;导致复杂场景下难以满足需求&#xff1a; 阻塞获取结果 必须通过future.get()阻塞线程等待结果&#xff0c;无法实现真正的非阻塞&#xff1a; Executo…...

基于FLask的共享单车需求数据可视化分析系统

【FLask】基于FLask的共享单车需求数据可视化分析系统 &#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统能够整合并处理大量共享单车使用数据&#xff0c;通过直观的可视化手段&#xff0…...

vue2项目中,多个固定的请求域名 和 通过url动态获取到的ip域名 封装 axios

vue2 使用场景&#xff1a;项目中&#xff0c;有固定的请求域名&#xff0c;而有某些接口是其他域名 /utils/request.js 固定请求域名 import axios from axios import Vue from vuelet baseURL switch (window.location.hostname) {case localhost: // 本地case 127.0.0.1…...

【嵌入式学习3】基于python的tcp客户端、服务器

目录 1、tcp客户端 2、tcp服务器 3、服务器多次连接客户端、多次接收信息 1、tcp客户端 """ tcp:客户端 1. 导入socket模块 2. 创建socket套接字 3. 建立tcp连接(和服务端建立连接) 4. 开始发送数据(到服务端) 5. 关闭套接字 """ import soc…...

tomcat构建源码环境

一. IDEA运行Tomcat8源码 参考网址&#xff1a;https://blog.csdn.net/yekong1225/article/details/81000446 ​ Tomcat作为J2EE的开源实现&#xff0c;其代码具有很高的参考价值&#xff0c;我们可以从中汲取很多的知识。作为Java后端程序员&#xff0c;相信有很多人很想了解…...

你用的是Bing吗?!

&#x1f525;【深度解析】微软Bing革命性升级&#xff01;Copilot Search上线&#xff1a;从此搜索≠找链接&#xff0c;而是直接生成答案&#xff01; &#x1f4a1; 你是否厌倦了这样的搜索体验&#xff1f; 搜索「Python处理JSON」&#xff0c;在10个网页间反复跳转想对比…...

拍摄的婚庆视频有些DAT的视频文件打不开怎么办

3-12 现在的婚庆公司大多提供结婚的拍摄服务&#xff0c;或者有一些第三方公司做这方面业务&#xff0c;对于视频拍摄来说&#xff0c;有时候会遇到这样一种问题&#xff0c;就是拍摄下来的视频文件&#xff0c;然后会有一两个视频文件是损坏的&#xff0c;播放不了&#xff0…...

Oracle Cloud (OCI) 服务器最新控制台启用 IPv6 超详细图文指南(2025最新实践)

本文为原作者发布到第三方平台,更多内容参考: 🚀 Oracle Cloud (OCI) 服务器最新控制台启用 IPv6 超详细图文指南(2025最新实践) 随着 IPv6 的普及,IPv6的优秀特性能为你的 OCI 云服务器提升网络性能和路由效率,并提升兼容性。本指南将引导你在 Oracle Cloud Infrast…...

YOLO环境搭建,win11+wsl2+ubuntu24+cuda12.6+idea

提示&#xff1a;环境搭建 文章目录 前言一、 win11 gpu 驱动更新1.1 下载驱动3. 验证&#xff0c; 二、配置子系统 ubuntu2.1 安装 cuda 三、配置 anaconda四、idea 配置使用 wsl ubuntu conda 环境 前言 提示&#xff1a;版本 win11 wsl2 ubuntu24 idea 2024 子系统跳过&…...

类 和 对象 的介绍

对象的本质是一种新的数据类型。类是一个模型&#xff0c;对象是类的一个具体化实例。为类创建实例也就是创建对象。 一、类&#xff08;class&#xff09; 类决定一个对象将是什么样的&#xff08;有什么属性、功能&#xff09;。类和变量一样&#xff0c;有名字。 1.创建类 …...

机器学习(1)—线性回归

文章目录 1. 算法定义2. 模型形式2.1. 简单线性回归&#xff08;单变量&#xff09;&#xff1a;2.2. 多元线性回归&#xff08;多变量&#xff09;&#xff1a; 3. 基本原理3.1. 误差函数&#xff1a;3.2. 求解回归系数 4. 假设条件5. 模型评估6. 优缺点7. 扩展方法8. 应用场景…...

macOS下SourceInsight的替代品

macOS 推荐的几款开源、轻量级、且功能类似于 SourceInsight 的源码阅读工具&#xff08;排除 VS Code&#xff09;&#xff1a; 1. Zeal&#xff08;离线文档 简单代码导航&#xff09; 官网/GitHub: https://zealdocs.org/特点&#xff1a; 轻量级离线文档浏览器&#xff0…...

form实现pdf文件转换成jpg文件

说明&#xff1a; 我希望将pdf文件转换成jpg文件 请去下载并安装 Ghostscript&#xff0c;gs10050w64.exe 配置环境变量&#xff1a;D:\Program Files\gs\gs10.05.0\bin 本地pdf路径&#xff1a;C:\Users\wangrusheng\Documents\name.pdf 输出文件目录&#xff1a;C:\Users\wan…...

聊天室项目之http知识

一.http的核心组成部分&#xff08;都分成请求的和响应的&#xff09; 1.起始行&#xff1a;请求------------------------ 方法&#xff08;Method&#xff09;&#xff1a;GET、POST、PUT、DELETE 等。 请求目标&#xff08;Request Target&#xff09;&#xff1a;URL 路径…...

kubeadm部署 Kubernetes(k8s) 高可用集群 V1.28.2

1. 安装要求 在开始之前&#xff0c;部署Kubernetes集群机器需要满足以下几个条件&#xff1a; 10台机器&#xff0c;操作系统Openeuler22.03 LTS SP4硬件配置&#xff1a;2GB或更多RAM&#xff0c;2个CPU或更多CPU&#xff0c;硬盘30GB或更多&#xff0c;docker 数据卷单独挂…...

BUUCTF-web刷题篇(12)

21.easy_tornado Tornado大致可以分为四个主要组成部分&#xff1a; 一个web框架&#xff08;包括RequestHandler创建Web应用程序的子类&#xff0c;以及各种支持类&#xff09;。 HTTPServerHTTP&#xff08;和AsyncHTTPClient&#xff09;的客户端和服务器端实现。 一个异…...

基于 Netty 框架的 Java TCP 服务器端实现,用于启动一个 TCP 服务器来处理客户端的连接和数据传输

代码&#xff1a; package com.example.tpson_tcp;import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; imp…...

【Kafka基础】Kafka配置文件关键参数解析与单机生产环境配置指南

1 Kafka配置文件概述 Apache Kafka的配置文件是控制其行为的关键所在&#xff0c;合理的配置能够显著提升性能、可靠性和可维护性。Kafka主要涉及两个核心配置文件&#xff1a; server.properties&#xff1a;Broker主配置文件zookeeper.properties&#xff1a;ZooKeeper配置文…...

Kafka 漏消费和重复消费问题

Kafka 虽然是一个高可靠、高吞吐的消息系统&#xff0c;但如果使用不当&#xff0c;**“漏消费”和“重复消费”**问题是非常容易发生的&#xff0c;尤其在业务系统中会造成数据丢失、重复写库等严重问题。 &#x1f3af; 一句话理解&#xff1a; Kafka 本身提供 “至多一次”…...

Mysql慢查询设置 和 建立索引

1 .mysql慢查询的设置 slow_query_log ON //或 slow_query_log_file /usr/local/mysql/data/slow.log long_query_time 2 修改后重启动mysql 1.1 查看设置后的参数 mysql> show variables like slow_query%; --------------------------------------------------…...

Windows程序中计时器WM_TIMER消息的使用

本文章是对《Windows程序设计》这本书第八章计时器的总结&#xff0c;如果有时间&#xff0c;可以去看书里的讲解&#xff0c;如果时间不充裕&#xff0c;想马上知道计时器该如何使用&#xff0c;欢迎阅读本文&#xff0c;本文已经将计时器的干货整理完毕&#xff01; 什么是计…...

关于apple ios苹果mdm监管锁的漏洞与修复

前言 本人从2020年开始接触苹果mdm管理系统的开发 起初只是接触如何利用mdm进行app分发 23年开始开发mdm监管锁业务 随着手机租赁的市场兴起 mdm监管锁系统随即而生 注意 本人只是分享工作过程中遇到的一些问题 不接受苹果手机屏蔽以及解锁的业务 此文章仅做分享使用 MDM监…...

使用Geotools中的原始方法来操作PostGIS空间数据库

目录 前言 一、原生PostGIS连接介绍 1、连接参数说明 2、创建DataStore 二、工程实战 1、Maven Pom.xml定义 2、空间数据库表 3、读取空间表的数据 三、总结 前言 在当今数字化与信息化飞速发展的时代&#xff0c;空间数据的处理与分析已成为众多领域不可或缺的一环。从…...

C-S模式之实现一对一聊天

天天开心&#xff01;&#xff01;&#xff01; 文章目录 一、如何实现一对一聊天&#xff1f;1. 服务器设计2. 客户端设计3. 服务端代码实现4. 客户端代码实现5. 实现说明6.实验结果 二、改进常见的服务器高并发方案1. 多线程/多进程模型2. I/O多路复用3. 异步I/O&#xff08;…...

Kafka Consumer Group

Kafka 消费者组&#xff08;Consumer Group&#xff09; 是 Kafka 的核心机制之一&#xff01;理解它对你掌握 Kafka 的高可用、高吞吐、负载均衡等能力非常关键。下面我来给你完整讲一讲&#x1f447; &#x1f9e0; 什么是 Kafka 消费者组&#xff08;Consumer Group&#x…...

LangChain vs LlamaIndex:大模型应用开发框架深度对比与实战指南

一、引言:大模型时代的应用开发挑战 随着ChatGPT、LLaMA等大语言模型的爆发式发展,如何高效构建「大模型+垂直领域」的智能应用成为新课题。传统开发模式面临三大痛点: 数据交互复杂:大模型与本地数据的融合缺乏标准化接口功能扩展困难:链式调用、工具集成需要重复造轮子…...

第二章:访问远程服务_《凤凰架构:构建可靠的大型分布式系统》

第二章 访问远程服务 2.1 远程服务调用&#xff08;RPC&#xff09; 2.1.1 进程间通信机制 核心方式&#xff1a; 管道&#xff08;Pipe&#xff09;&#xff1a;单向通信&#xff0c;用于父子进程信号&#xff08;Signal&#xff09;&#xff1a;异步事件通知&#xff0c;不…...

一键自动备份:数据安全的双重保障

随着数字化时代的到来&#xff0c;数据已成为企业和个人不可或缺的核心资产。在享受数据带来的便捷与高效的同时&#xff0c;数据丢失的风险也随之增加。因此&#xff0c;备份文件的重要性不言而喻。本文将深入探讨备份文件的重要性&#xff0c;并介绍两种实用的自动备份方法&a…...

C++11之std::is_convertible

目录 1.简介 2.实现原理 3.使用场景 4.总结 1.简介 std::is_convertible 是 C 标准库 <type_traits> 头文件中的一个类型特性&#xff08;type trait&#xff09;&#xff0c;它用于在编译时检查一个类型是否可以隐式转换为另一个类型。下面的原型&#xff1a; temp…...

从零开始:在Qt中使用OpenGL绘制指南

从零开始&#xff1a;在Qt中使用OpenGL绘制指南 本文只介绍基本的 QOpenGLWidget 和 QOpenGLFunctions 的使用&#xff0c;想要学习 OpenGL 的朋友&#xff0c;建议访问经典 OpenGL 学习网站&#xff1a;LearnOpenGL CN 本篇文章&#xff0c;我们将以绘制一个经典的三角形为例&…...

我的购物车设计思考:从个人项目到生产实战思考的蜕变

一、代码初体验&#xff1a;我踩过的那些坑 还记得大二做课程设计时&#xff0c;我写的购物车直接用ArrayList存商品&#xff0c;结果改数量时遍历半天找商品。现在看你这个HashMap实现&#xff0c;确实清爽很多&#xff0c;但有几点让我想起当年惨痛经历&#xff1a; 1. 线程…...

【算法实践】算法面试常见问题——数组的波浪排序

问题描述 给定一个无序整数数组&#xff0c;将其排列成波浪形数组。若数组 arr[0..n-1] 满足以下条件&#xff0c;则称为波浪形&#xff1a; arr[0] > arr[1] < arr[2] > arr[3] < arr[4] > ... 或 arr[0] < arr[1] > arr[2] < arr[3] > arr[4] &l…...

【大模型深度学习】如何估算大模型需要的显存

一、模型参数量 参数量的单位 参数量指的是模型中所有权重和偏置的数量总和。在大模型中&#xff0c;参数量的单位通常以“百万”&#xff08;M&#xff09;或“亿”&#xff08;B&#xff0c;也常说十亿&#xff09;来表示。 百万&#xff08;M&#xff09;&#xff1a;表示…...

[论文阅读]PMC-LLaMA: Towards Building Open-source Language Models for Medicine

PMC-LLaMA&#xff1a;构建医学开源语言模型 摘要 最近&#xff0c;大语言模型在自然语言理解方面展现了非凡的能力。尽管在日常交流和问答场景下表现很好&#xff0c;但是由于缺乏特定领域的知识&#xff0c;这些模型在需要精确度的领域经常表现不佳&#xff0c;例如医学应用…...

`use_tempaddr` 和 `temp_valid_lft ` 和 `temp_prefered_lft ` 笔记250405

use_tempaddr 和 temp_valid_lft 和 temp_prefered_lft 笔记250405 以下是 Linux 系统中与 IPv6 临时隐私地址相关的三个关键参数 use_tempaddr、temp_valid_lft 和 temp_prefered_lft 的详细说明及协作关系&#xff1a; &#x1f4dc; 参数定义与功能 参数作用默认值依赖关…...

如何设置 JVM 内存参数(-Xms、-Xmx、-Xss 等)?

JVM 内存参数用于控制 Java 虚拟机使用的内存大小和行为。以下是一些常用的 JVM 内存参数及其设置方法&#xff1a; 1. 堆内存 (Heap Memory): -Xms<size>: 设置 JVM 初始堆大小 (initial heap size)。 例如&#xff1a;-Xms2g (初始堆大小为 2GB)默认值&#xff1a;物…...

【MATLAB TCP/IP客户端与NetAssist上位机双向通信实战指南】

MATLAB TCP/IP客户端与NetAssist上位机双向通信实战指南 一、前言 在工业控制和数据采集领域&#xff0c;TCP/IP通信是最常用的网络通信协议之一。MATLAB作为强大的科学计算软件&#xff0c;与各种上位机软件(如NetAssist)进行通信可以实现数据采集、设备控制和实时监控等功能…...

联合、枚举、类型别名

数据类型&#xff1a; 已学--整数、实数、字符、字符串、数组、指针、结构待学--向量&#xff08;vector&#xff09;类型&#xff1a;优于数组非主流的类型--联合&#xff08;union&#xff09;、枚举&#xff08;enum&#xff09; 一、联合 联合类似于结构&#xff0c;可以容…...

Array 和 ArrayList 有何区别?什么时候更适合用 Array?

面试官提问&#xff1a; 你能简要说明 Array 和 ArrayList 之间的主要区别吗&#xff1f;在什么场景下更适合使用 Array&#xff1f; 标准回答&#xff1a; 在 Java 中&#xff0c;Array&#xff08;数组&#xff09;和 ArrayList&#xff08;动态数组&#xff09;都可以用于存…...

对状态模式的理解

对状态模式的理解 一、场景二、不采用状态模式1、代码2、缺点 三、采用状态模式1、代码1.1 状态类1.2 上下文&#xff08;这里指&#xff1a;媒体播放器&#xff09;1.3 客户端 2、优点 一、场景 同一个东西&#xff08;例如&#xff1a;媒体播放器&#xff09;&#xff0c;有一…...

【学Rust写CAD】31 muldiv255函数(muldiv255.rs)

源码 // Calculates floor(a*b/255 0.5) #[inline] pub fn muldiv255(a: u32, b: u32) -> u32 {// The deriviation for this formula can be// found in "Three Wrongs Make a Right" by Jim Blinn.let tmp a * b 128;(tmp (tmp >> 8)) >> 8 }代…...

使用VSCode编写C#程序

目录 一、环境搭建&#xff1a;构建高效开发基础1. 安装VSCode2. 配置.NET SDK3. 安装核心扩展 二、项目开发全流程1. 创建项目2. 代码编辑技巧3. 调试配置4. 高级调试技巧5. 编译与运行 三、常见问题解决指南1. 项目加载失败2. IntelliSense失效3. 代码格式化4. 典型编译错误&…...

chromadb

chromadb是一个轻量化的向量数据库&#xff0c;可以和llama-index等RAG框架使用。底层基于sqllite。 Getting Started - Chroma Docs 1、安装 $pip install chromadb pip install chromadb-client --在CS模式下&#xff0c;如果机器A上只需要安装客户端 2、可以使用客户端…...

第十章: 可观测性_《凤凰架构:构建可靠的大型分布式系统》

第十章: 可观测性 可观测性是现代分布式系统监控和故障排查的核心能力。本章从事件日志、链路追踪、聚合度量三个维度构建完整的可观测性体系&#xff0c;以下是各部分的重点解析与实践要点&#xff1a; 一、事件日志&#xff08;Event Logging&#xff09; 1. 核心目标 全链…...

vscode和cursor对ubuntu22.04的remote ssh和X-Windows的无密码登录

这里写自定义目录标题 写在前面需求的描述问题的引出 昨天已使能自动登录上午我的改变UBUNTU 22.04关闭密码规则一&#xff1a;修改 /etc/pam.d/common-password 文件二&#xff1a;修改 /etc/security/pwquality.conf 文件方法三&#xff1a;禁用 pam_pwquality.so 模块 vscod…...

Mlivus Cloud SDK v2的革新:性能优化与实战解析

作为大禹智库的向量数据库高级研究员王帅旭,我在过去30多年的AI应用实战中见证了向量数据库技术的演进历程。今天,我将从专业角度深入剖析Mlivus Cloud SDK v2的架构革新,特别是针对性能瓶颈问题的突破性解决方案。本文不仅会详细解析技术原理,还将提供可操作的优化建议,帮…...