数据结构与算法|第十四章:排序算法(上)— 比较类排序

数据结构与算法|排序算法(上)--- 比较类排序

  • [第十四章 排序算法(上)--- 比较类排序](#第十四章 排序算法(上)— 比较类排序)
    • [14.1 排序算法的基本概念](#14.1 排序算法的基本概念)
      • [14.1.1 什么是排序](#14.1.1 什么是排序)
      • [14.1.2 排序算法的评价指标](#14.1.2 排序算法的评价指标)
      • [14.1.3 排序算法的分类](#14.1.3 排序算法的分类)
    • [14.2 冒泡排序(Bubble Sort)](#14.2 冒泡排序(Bubble Sort))
      • [14.2.1 基本思想](#14.2.1 基本思想)
      • [14.2.2 基础实现](#14.2.2 基础实现)
      • [14.2.3 优化方案](#14.2.3 优化方案)
    • [14.3 选择排序(Selection Sort)](#14.3 选择排序(Selection Sort))
      • [14.3.1 基本思想](#14.3.1 基本思想)
      • [14.3.2 代码实现](#14.3.2 代码实现)
    • [14.4 插入排序(Insertion Sort)](#14.4 插入排序(Insertion Sort))
      • [14.4.1 基本思想](#14.4.1 基本思想)
      • [14.4.2 代码实现](#14.4.2 代码实现)
    • [14.5 希尔排序(Shell Sort)](#14.5 希尔排序(Shell Sort))
      • [14.5.1 基本思想](#14.5.1 基本思想)
      • [14.5.2 代码实现](#14.5.2 代码实现)
    • [14.6 归并排序(Merge Sort)](#14.6 归并排序(Merge Sort))
      • [14.6.1 自顶向下归并排序](#14.6.1 自顶向下归并排序)
      • [14.6.2 自底向上归并排序](#14.6.2 自底向上归并排序)
    • [14.7 快速排序(Quick Sort)](#14.7 快速排序(Quick Sort))
      • [14.7.1 单路快排](#14.7.1 单路快排)
      • [14.7.2 双路快排](#14.7.2 双路快排)
      • [14.7.3 三路快排](#14.7.3 三路快排)
    • 总结与预告

上篇:第十三章、递归与分治

下篇:第十五章、排序算法(下)--- 非比较类排序

第十四章 排序算法(上)--- 比较类排序

在前面的章节中,我们从数组、链表到树、图,一路学习了各种数据结构的组织与操作方式。但有一个贯穿始终的问题我们迟迟没有系统讨论------"如何将一组数据按特定顺序排列"

排序(Sorting)是计算机科学中最基础、研究最深入的算法问题之一。据统计,商业数据处理系统中约 25% 的 CPU 周期 花在了排序上。从数据库的 ORDER BY 到搜索引擎的排名,从操作系统的进程调度到 Excel 表格的排序按钮------排序无处不在。

本章作为排序专题的上篇,聚焦 基于比较的排序算法 ,它们有一个共同特征:通过比较元素之间的大小关系来决定元素的相对顺序。我们将从最直观的冒泡排序出发,一路深入到快速排序和归并排序,建立完整的比较类排序知识体系。


14.1 排序算法的基本概念

14.1.1 什么是排序

排序(Sorting):将一个数据元素的任意序列,重新排列成一个按关键字有序的序列。

设含有 n n n 个记录的序列为:

{ R 1 , R 2 , ... , R n } \{R_1, R_2, \dots, R_n\} {R1,R2,...,Rn}

其对应的关键字序列为 { k 1 , k 2 , ... , k n } \{k_1, k_2, \dots, k_n\} {k1,k2,...,kn}。排序就是确定一种排列 p 1 , p 2 , ... , p n p_1, p_2, \dots, p_n p1,p2,...,pn,使得:

k p 1 ≤ k p 2 ≤ ⋯ ≤ k p n (递增排序) k_{p_1} \le k_{p_2} \le \dots \le k_{p_n} \quad(递增排序) kp1≤kp2≤⋯≤kpn(递增排序)

或者:

k p 1 ≥ k p 2 ≥ ⋯ ≥ k p n (递减排序) k_{p_1} \ge k_{p_2} \ge \dots \ge k_{p_n} \quad(递减排序) kp1≥kp2≥⋯≥kpn(递减排序)

14.1.2 排序算法的评价指标

评价一个排序算法,通常从以下五个维度考量:

评价指标 含义 说明
时间复杂度 最好 / 平均 / 最坏情况下的比较和移动次数 最核心的指标
空间复杂度 除输入数据外额外占用的内存 原地排序(In-place) 空间为 O(1)
稳定性 相等元素的相对顺序在排序后是否保持不变 见下方详解
比较次数 元素之间比较的总次数 与数据初始状态有关
移动次数 元素实际交换或移动的总次数 影响常数因子

稳定性详解 :假设数组中有两个相等的元素 A 和 B(A 原本在 B 前面)。如果排序后 A 仍然在 B 前面,则算法是稳定的 ;否则是不稳定的。稳定性在多关键字排序(如先按成绩排序,再按姓名排序)中至关重要。

14.1.3 排序算法的分类

分类维度 类型 代表算法
是否基于比较 比较类排序 冒泡、选择、插入、希尔、归并、快速、堆排序
非比较类排序 计数排序、桶排序、基数排序
是否原地 原地排序(In-place) 冒泡、选择、插入、希尔、快速、堆排序
非原地排序 归并排序
是否稳定 稳定排序 冒泡、插入、归并、计数、桶、基数
不稳定排序 选择、希尔、快速、堆排序

本章聚焦比较类排序的前六种(堆排序放在第 9 章已介绍),非比较类排序将在第 15 章展开。


14.2 冒泡排序(Bubble Sort)

14.2.1 基本思想

冒泡排序:重复遍历数组,依次比较相邻的两个元素 ,如果它们的顺序错误就交换。每一轮遍历会将当前未排序部分的最大值"浮"到末尾------就像水中的气泡缓缓上升。

核心操作:相邻比较 + 交换。每一轮确定一个元素的最终位置。

14.2.2 基础实现

java 复制代码
/**
 * 冒泡排序(基础版)
 * 时间复杂度:最好 O(n)、平均 O(n²)、最坏 O(n²)
 * 空间复杂度:O(1) ------ 原地排序
 * 稳定性:稳定(相等元素不交换)
 */
public void bubbleSort(int[] arr) {
    int n = arr.length;
    for (int i = 0; i < n - 1; i++) {           // 共 n-1 轮
        for (int j = 0; j < n - 1 - i; j++) {   // 每轮比较到未排序区末尾
            if (arr[j] > arr[j + 1]) {          // 相邻比较
                swap(arr, j, j + 1);            // 逆序则交换
            }
        }
    }
}

private void swap(int[] arr, int i, int j) {
    int tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}

14.2.3 优化方案

优化一:提前终止。如果某一轮没有发生任何交换,说明数组已有序,可以提前结束。

java 复制代码
/**
 * 冒泡排序(优化版:提前终止)
 */
public void bubbleSortOptimized(int[] arr) {
    int n = arr.length;
    for (int i = 0; i < n - 1; i++) {
        boolean swapped = false;                 // 本轮是否发生交换
        for (int j = 0; j < n - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr, j, j + 1);
                swapped = true;
            }
        }
        if (!swapped) break;                     // 无交换 → 已有序
    }
}

优化二:记录最后交换位置。每轮记录最后一次交换的位置,该位置之后已经有序。

java 复制代码
/**
 * 冒泡排序(优化版:记录最后交换位置)
 */
public void bubbleSortOptimized2(int[] arr) {
    int n = arr.length;
    int lastSwapPos = n - 1;                     // 最后一轮比较的终点
    while (lastSwapPos > 0) {
        int newBoundary = 0;                     // 新的有序边界
        for (int j = 0; j < lastSwapPos; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr, j, j + 1);
                newBoundary = j;                 // 更新最后交换位置
            }
        }
        lastSwapPos = newBoundary;               // 下一轮只比较到此处
    }
}

算法分析 :冒泡排序在最好情况 (已有序)下只需 O(n);最坏情况(逆序)需 O(n²);平均 O(n²)。虽然思路简单,但因交换次数多,实际性能在 O ( n 2 ) O(n^2) O(n2) 算法中最差。


14.3 选择排序(Selection Sort)

14.3.1 基本思想

选择排序:将数组分为已排序区(前部)未排序区(后部) 。每一轮从未排序区中选出最小值,放到已排序区的末尾。

核心操作:选择最小值 + 交换到位。每一轮确定一个元素(最小值)的最终位置。

复制代码
初始: [5, 3, 8, 1, 9]
       ↑未排序区

第1轮: 选最小 1 → 与位置0交换 → [1, 3, 8, 5, 9]
       ↑已排序  ↑未排序区

第2轮: 选最小 3 → 与位置1交换 → [1, 3, 8, 5, 9]
          ↑已排序  ↑未排序区

第3轮: 选最小 5 → 与位置2交换 → [1, 3, 5, 8, 9]
             ↑已排序  ↑未排序区

第4轮: 选最小 8 → 与位置3交换 → [1, 3, 5, 8, 9]
                ↑已排序区

14.3.2 代码实现

java 复制代码
/**
 * 选择排序
 * 时间复杂度:O(n²)(最好 = 平均 = 最坏)
 * 空间复杂度:O(1) ------ 原地排序
 * 稳定性:不稳定(跳跃式交换可能破坏顺序)
 */
public void selectionSort(int[] arr) {
    int n = arr.length;
    for (int i = 0; i < n - 1; i++) {
        int minIndex = i;                        // 假设当前位置是最小值
        for (int j = i + 1; j < n; j++) {       // 在未排序区中寻找更小值
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        if (minIndex != i) {                     // 将最小值交换到已排序区末尾
            swap(arr, i, minIndex);
        }
    }
}

算法分析 :无论数据初始状态如何,选择排序的比较次数固定为 n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n−1),所以最好 = 平均 = 最坏 = O(n²) 。优点是交换次数少(最多 n−1 次),在某些写操作昂贵的场景(如 Flash 存储)中有优势。不稳定示例[5, 8, 5, 2] 排序后两个 5 的相对顺序发生改变。


14.4 插入排序(Insertion Sort)

14.4.1 基本思想

插入排序:将数组分为已排序区(前部)未排序区(后部) 。每次从未排序区取出第一个元素,在已排序区中从后往前扫描 ,找到合适位置插入

类比:打扑克牌时,你摸到一张新牌,从右往左在手中已有的牌中找到合适位置插入。

复制代码
初始: [5 | 3, 8, 1, 9]
      已排序 ↑未排序区

第1轮: 取 3 → 插入到 5 前面    → [3, 5 | 8, 1, 9]
第2轮: 取 8 → 插入到 5 后面    → [3, 5, 8 | 1, 9]
第3轮: 取 1 → 插入到最前面     → [1, 3, 5, 8 | 9]
第4轮: 取 9 → 插入到末尾       → [1, 3, 5, 8, 9]

14.4.2 代码实现

java 复制代码
/**
 * 插入排序
 * 时间复杂度:最好 O(n)、平均 O(n²)、最坏 O(n²)
 * 空间复杂度:O(1) ------ 原地排序
 * 稳定性:稳定(相等元素不会跨越插入)
 */
public void insertionSort(int[] arr) {
    int n = arr.length;
    for (int i = 1; i < n; i++) {                // 从第二个元素开始
        int key = arr[i];                        // 待插入的元素
        int j = i - 1;
        // 在已排序区中从后往前找插入位置
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];                 // 比 key 大的元素后移
            j--;
        }
        arr[j + 1] = key;                        // 插入到正确位置
    }
}

算法分析 :插入排序对近乎有序 的数据表现极佳------最好情况 O(n)。在数据量较小(n < 50)时,插入排序的常数因子极小,甚至比快速排序更快。这也是为什么快速排序在小规模子问题上会切换到插入排序


14.5 希尔排序(Shell Sort)

14.5.1 基本思想

希尔排序:插入排序的改进版。核心思路是先让数组**"宏观有序",再逐步细化。通过引入增量(gap)**,将数组分成若干个子序列分别进行插入排序;随着增量逐渐减小至 1,最终完成一次标准的插入排序。
为什么比直接插入排序快:当 gap 较大时,元素可以"大步跳跃"到接近最终位置,减少了后续 gap=1 时的移动量。
gap=1 --- 标准插入排序
gap=3 --- 分成3组

5, _, _, 1, _, _

_, 3, _, _, 9, _

_, _, 8, _, _, 2

1, 3, 2, 5, 9, 8

14.5.2 代码实现

java 复制代码
/**
 * 希尔排序(使用 Knuth 增量序列:1, 4, 13, 40, 121, ...)
 * 时间复杂度:取决于增量序列,Knuth 序列为 O(n^{3/2})
 * 空间复杂度:O(1) ------ 原地排序
 * 稳定性:不稳定(分组跳跃可能打乱相等元素的相对顺序)
 */
public void shellSort(int[] arr) {
    int n = arr.length;
    // 计算 Knuth 最大增量:h = 3*h + 1
    int gap = 1;
    while (gap < n / 3) {
        gap = gap * 3 + 1;                       // 1, 4, 13, 40, 121, ...
    }

    while (gap > 0) {
        // 对每个 gap 分组进行插入排序
        for (int i = gap; i < n; i++) {
            int key = arr[i];
            int j = i;
            while (j >= gap && arr[j - gap] > key) {
                arr[j] = arr[j - gap];
                j -= gap;
            }
            arr[j] = key;
        }
        gap /= 3;                                // 缩小增量
    }
}

算法分析 :希尔排序的时间复杂度与增量序列密切相关 。最朴素的希尔增量( n / 2 , n / 4 , ... , 1 n/2, n/4, \dots, 1 n/2,n/4,...,1)最坏为 O ( n 2 ) O(n^2) O(n2);Hibbard 增量( 1 , 3 , 7 , 15 , ... 1, 3, 7, 15, \dots 1,3,7,15,...,即 2 k − 1 2^k - 1 2k−1)最坏为 O ( n 3 / 2 ) O(n^{3/2}) O(n3/2);Knuth 增量( 1 , 4 , 13 , 40 , ... 1, 4, 13, 40, \dots 1,4,13,40,...,即 h = 3 h + 1 h = 3h + 1 h=3h+1)同样为 O ( n 3 / 2 ) O(n^{3/2}) O(n3/2);Sedgewick 增量可达 O ( n 4 / 3 ) O(n^{4/3}) O(n4/3)。希尔排序是第一个突破 O ( n 2 ) O(n^2) O(n2) 的排序算法,在小到中等规模数据上非常实用。


14.6 归并排序(Merge Sort)

14.6.1 自顶向下归并排序

归并排序分治法 的经典应用------将数组不断二分,直到每个子数组只有一个元素(天然有序),然后两两合并有序子数组。

在第 13 章我们已经实现过归并排序,此处换个角度------用 自底向上(迭代版) 展示另一种思路。

java 复制代码
/**
 * 归并排序(自顶向下 --- 递归版)
 * 时间复杂度:严格 O(n log n)
 * 空间复杂度:O(n)(辅助数组)
 * 稳定性:稳定(合并时相等元素保持原顺序)
 */
public void mergeSort(int[] arr) {
    int[] temp = new int[arr.length];
    mergeSortRec(arr, 0, arr.length - 1, temp);
}

private void mergeSortRec(int[] arr, int left, int right, int[] temp) {
    if (left >= right) return;

    int mid = left + (right - left) / 2;
    mergeSortRec(arr, left, mid, temp);
    mergeSortRec(arr, mid + 1, right, temp);
    merge(arr, left, mid, right, temp);
}

/** 合并两个有序子数组 [left, mid] 和 [mid+1, right] */
private void merge(int[] arr, int left, int mid, int right, int[] temp) {
    int i = left, j = mid + 1, k = left;
    while (i <= mid && j <= right) {
        temp[k++] = (arr[i] <= arr[j]) ? arr[i++] : arr[j++];
    }
    while (i <= mid)  temp[k++] = arr[i++];
    while (j <= right) temp[k++] = arr[j++];

    for (i = left; i <= right; i++) {
        arr[i] = temp[i];
    }
}

14.6.2 自底向上归并排序

自底向上(Bottom-Up)归并排序不使用递归,直接从单个元素开始,逐层合并:

java 复制代码
/**
 * 归并排序(自底向上 --- 迭代版)
 * 时间复杂度:严格 O(n log n)
 * 空间复杂度:O(n)
 * 稳定性:稳定
 */
public void mergeSortBottomUp(int[] arr) {
    int n = arr.length;
    int[] temp = new int[n];

    // sz:当前合并的子数组大小(1, 2, 4, 8, ...)
    for (int sz = 1; sz < n; sz *= 2) {
        for (int left = 0; left < n - sz; left += sz * 2) {
            int mid = left + sz - 1;
            int right = Math.min(left + sz * 2 - 1, n - 1);
            merge(arr, left, mid, right, temp);
        }
    }
}

迭代版核心逻辑sz=1 时,将相邻的单个元素合并为长度为 2 的有序对;sz=2 时,将相邻的长度为 2 的有序对合并为长度为 4 的有序段......直到 sz >= n
sz=4
sz=2
sz=1

5\] \[3\] → \[3,5

8\] \[1\] → \[1,8

9\] \[2\] → \[2,9

3,5\] \[1,8\] → \[1,3,5,8

2,9\] 单独留下 \[1,3,5,8\] \[2,9\] → \[1,2,3,5,8,9

算法分析 :归并排序的时间复杂度无论最好、最坏还是平均都是 O(n log n) ------这是它在基于比较的排序中最大的优势之一。缺点是空间复杂度 O(n),不是原地排序。自底向上版本避免了递归调用开销,在链表排序 (如 JDK 中 java.util.Arrays 对对象数组的排序)和外部排序(磁盘文件排序)中应用广泛。

自顶向下 vs 自底向上:

维度 自顶向下(递归) 自底向上(迭代)
实现方式 递归 + 分治 迭代 + 倍增
代码可读性 更直观 稍复杂
递归栈开销 O(log n) 栈空间 无递归开销
适合场景 理解分治思想 链表排序、避免 StackOverflow

14.7 快速排序(Quick Sort)

快速排序是实际应用中最快的通用排序算法------它是 Java 中对基本类型数组 排序的默认算法(java.util.DualPivotQuicksort)。核心思想已在第 13 章介绍,本章重点讨论三种分区策略的演进。

14.7.1 单路快排

单路快排:选一个 pivot,用单指针分区------遍历数组,将小于等于 pivot 的元素放到前面,最后 pivot 归位。

java 复制代码
/**
 * 快速排序(单路快排)
 * 时间复杂度:平均 O(n log n),最坏 O(n²)
 * 空间复杂度:O(log n)(递归栈)
 * 稳定性:不稳定
 */
public void quickSort(int[] arr, int low, int high) {
    if (low >= high) return;
    int p = partition(arr, low, high);
    quickSort(arr, low, p - 1);
    quickSort(arr, p + 1, high);
}

/** 单路分区:将 [low, high] 分为 <= pivot 和 > pivot 两部分 */
private int partition(int[] arr, int low, int high) {
    int pivot = arr[low];                        // 选第一个元素为 pivot
    int j = low;                                 // j 指向 <= pivot 区域的末尾
    for (int i = low + 1; i <= high; i++) {
        if (arr[i] <= pivot) {
            j++;
            swap(arr, i, j);
        }
    }
    swap(arr, low, j);                           // pivot 归位
    return j;
}

单路快排的问题 :当数组中存在大量重复元素 时,分区极不平衡(所有等于 pivot 的元素都分到一侧),导致在重复元素极多的数组上退化为 O(n²)。

14.7.2 双路快排

双路快排:使用左右双指针 ,分别从两端向中间扫描,将严格小于 pivot 的元素放左边、严格大于 pivot 的元素放右边 ,等于 pivot 的元素均匀分散到两侧。

java 复制代码
/**
 * 快速排序(双路快排)
 * 时间复杂度:平均 O(n log n),最坏 O(n²)
 * 空间复杂度:O(log n)
 * 稳定性:不稳定
 */
public void quickSort2Ways(int[] arr, int low, int high) {
    if (low >= high) return;
    int p = partition2Ways(arr, low, high);
    quickSort2Ways(arr, low, p - 1);
    quickSort2Ways(arr, p + 1, high);
}

/** 双路分区 */
private int partition2Ways(int[] arr, int low, int high) {
    // 随机选 pivot 并交换到首位,避免有序数组时退化
    int randomIdx = low + (int)(Math.random() * (high - low + 1));
    swap(arr, low, randomIdx);

    int pivot = arr[low];
    int left = low + 1;
    int right = high;

    while (true) {
        while (left <= right && arr[left] < pivot)  left++;   // 找左边 >= pivot 的
        while (left <= right && arr[right] > pivot) right--;  // 找右边 <= pivot 的
        if (left >= right) break;
        swap(arr, left, right);
        left++;
        right--;
    }
    swap(arr, low, right);                       // pivot 归位
    return right;
}

双路快排的优势 :等于 pivot 的元素被均匀分布到两侧,在大量重复元素场景下不会导致分区倾斜,避免了退化为 O(n²)。

14.7.3 三路快排

三路快排:将数组分为三段 ------小于 pivot、等于 pivot、大于 pivot。递归时跳过等于 pivot 的中间段

java 复制代码
/**
 * 快速排序(三路快排)
 * 时间复杂度:平均 O(n log n),大量重复元素时可接近 O(n)
 * 空间复杂度:O(log n)
 * 稳定性:不稳定
 */
public void quickSort3Ways(int[] arr, int low, int high) {
    if (low >= high) return;

    // 随机选 pivot 并交换到首位
    int randomIdx = low + (int)(Math.random() * (high - low + 1));
    swap(arr, low, randomIdx);

    int pivot = arr[low];
    int lt = low;        // [low, lt] 小于 pivot 的区域
    int gt = high + 1;   // [gt, high] 大于 pivot 的区域
    int i = low + 1;     // 当前扫描指针

    while (i < gt) {
        if (arr[i] < pivot) {
            lt++;
            swap(arr, i, lt);
            i++;
        } else if (arr[i] > pivot) {
            gt--;
            swap(arr, i, gt);
        } else {
            i++;         // 等于 pivot,直接跳过
        }
    }
    swap(arr, low, lt);  // pivot 归位,此时 [lt, gt-1] 全等于 pivot

    quickSort3Ways(arr, low, lt - 1);   // 递归处理 < pivot 的部分
    quickSort3Ways(arr, gt, high);      // 递归处理 > pivot 的部分
    // 等于 pivot 的部分 [lt, gt-1] 已经就位,无需处理!
}

三路快排核心lt 指向小于区域的右边界,gt 指向大于区域的左边界。i 从左往右扫描,遇到小于 pivot 的往左扔,遇到大于 pivot 的往右扔,等于的直接走。扫描结束时 [lt+1, gt-1] 全是等于 pivot 的元素,已经排好序了。
分区结果
< pivot
=== pivot ===
> pivot
递归排序
递归排序

三种快排对比:

维度 单路快排 双路快排 三路快排
核心思路 单指针遍历 双指针夹逼 三指针划分三段
等于 pivot 的元素 全在一侧 均匀分散两侧 集中在中间
大量重复元素 ❌ 退化 O(n²) ✅ O(n log n) 接近 O(n)
有序数组 ❌ 退化 ✅ 随机 pivot 避免 ✅ 随机 pivot 避免
实现复杂度 简单 中等 稍复杂
推荐场景 学习理解 通用场景 大量重复元素

总结与预告

本章系统学习了六种基于比较的排序算法,它们的技术路线和适用场景各不相同:

排序算法 最好时间 平均时间 最坏时间 空间 稳定性 核心思想
冒泡排序 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^{3/2}) O(n²) O(1) ❌ 不稳 gap 分组 + 插入排序
归并排序 O(n log n) O(n log n) O(n log n) O(n) ✅ 稳定 分治法,先分后合
快速排序 O(n log n) O(n log n) O(n²) O(log n) ❌ 不稳 分治法,先分区后递归

选型建议指南:

  • 数据量小(n < 50) → 插入排序(常数因子最小)
  • 数据近乎有序 → 插入排序或冒泡排序(优化版)
  • 需要稳定排序 → 归并排序、冒泡排序、插入排序
  • 内存敏感(原地排序) → 快速排序、希尔排序
  • 通用场景、追求平均性能快速排序(双路/三路)------它是 Java 基本类型排序的默认选择
  • 链表排序 → 归并排序(自底向上,天然适配链表的 O(1) 合并)
  • 外部排序(磁盘文件) → 归并排序

本章的六种算法和下一章的非比较类排序(计数排序、桶排序、基数排序)共同构成了排序算法的完整版图。下一章我们将学习那三种不依赖比较就能排序的"魔法"算法------它们能在 O(n) 时间内完成排序!


上篇:第十三章、递归与分治

下篇:第十五章、排序算法(下)--- 非比较类排序

相关推荐
笨笨饿1 小时前
#72_聊聊I2C以及他们的变体
linux·c语言·网络·stm32·单片机·算法·个人开发
机器人图像处理1 小时前
6-自动白平衡(灰度世界算法)
opencv·算法·相机
Dr.Zeus1 小时前
从电芯到系统:BMS算法视角下的电池热管理深度解析作者署名
算法·能源
ulias2121 小时前
leetcode热题 - 6
linux·算法·leetcode
北顾笙9801 小时前
day42-数据结构力扣
数据结构
七颗糖很甜2 小时前
卫星通信遇到“太空天气”会怎样---电离层闪烁对卫星通信的影响
大数据·python·算法
风筝在晴天搁浅2 小时前
阿里淘天/京东 CodeTop LeetCode103.二叉树的锯齿形层序遍历
数据结构
小凡子空白在线学习2 小时前
工作拆分so总结
java·jvm·算法
88号技师2 小时前
2026年2月新锐一区SCI-完整家庭互动优化算法Undivided Family Interaction Algorithm-附Matlab免费代码
开发语言·算法·数学建模·matlab·优化算法