排序算法总结

一、O (n²) 时间复杂度的排序算法(简单排序)

这类算法逻辑简单,适合小规模数据,但效率较低,核心是通过 "比较 - 交换" 逐步缩小无序范围。

1. 冒泡排序(Bubble Sort)
  • 方法
    从列表头部开始,重复比较相邻的两个元素,若前者大于后者则交换位置,直到最大的元素 "冒泡" 到列表末尾;然后缩小范围,对剩余元素重复操作。
    • 优化:若某一轮未发生交换,说明列表已有序,可提前结束。
  • 复杂度
    • 时间:最佳 O (n)(已排序),最坏 / 平均 O (n²)
    • 空间:O (1)(原地排序)
  • 特点:稳定排序(相等元素相对位置不变),适用于几乎有序的数据。
2. 选择排序(Selection Sort)
  • 方法
    每次从无序区中找到最小(或最大)的元素,将其与无序区的第一个元素交换,使无序区范围缩小 1,直到全部有序。
  • 复杂度
    • 时间:最佳 / 最坏 / 平均 O (n²)(无论是否有序,都需遍历找最值)
    • 空间:O (1)(原地排序)
  • 特点 :不稳定(例如 [3, 2, 2],第一个 2 可能被交换到第二个 2 后面),交换次数少(仅 n-1 次)。
3. 插入排序(Insertion Sort)
  • 方法
    将列表分为 "有序区" 和 "无序区",每次从无序区取一个元素,插入到有序区的合适位置(通过比较和后移元素),逐步扩大有序区。
    • 类比:整理扑克牌时,按顺序将新牌插入已有序的牌堆中。
  • 复杂度
    • 时间:最佳 O (n)(已排序),最坏 / 平均 O (n²)
    • 空间:O (1)(原地排序)
  • 特点:稳定排序,适用于小规模数据或部分有序数据(如接近有序的数组)。

二、O (n log n) 时间复杂度的排序算法(高效排序)

这类算法通过 "分治""归并" 等思想突破 O (n²) 瓶颈,适合大规模数据。

1. 快速排序(Quick Sort)
  • 方法
    1. 选一个 "基准值"(如第一个元素),通过分区操作将列表分为两部分:左边元素均小于基准值,右边均大于基准值(基准值归位)。
    2. 递归对左右两部分重复上述操作,直到子列表长度为 1(天然有序)。
  • 复杂度
    • 时间:最佳 / 平均 O (n log n),最坏 O (n²)(基准值选到最值,如已排序数组)
      • 优化:随机选择基准值或三数取中(避免最坏情况)。
    • 空间:O (log n)~O (n)(递归栈开销,平均 log n,最坏 n)
  • 特点:不稳定排序,实际应用中最快的排序之一(缓存友好,原地排序)。
2. 归并排序(Merge Sort)
  • 方法

    1. 分:将列表递归拆分为两个子列表,直到子列表长度为 1。
    2. 合:将两个有序子列表 "归并" 为一个有序列表(通过双指针比较,依次取较小元素)。
    • 类比:将两堆已排序的书合并为一堆,每次从两堆顶取较小的一本。
  • 复杂度

    • 时间:最佳 / 最坏 / 平均 O (n log n)(不受数据分布影响)
    • 空间:O (n)(需额外数组存储归并结果)
  • 特点:稳定排序,适合大规模数据或链表排序(链表归并无需额外空间)。

3. 堆排序(Heap Sort)
  • 方法

    1. 将列表构建为大顶堆(父节点 >= 子节点)。
    2. 每次将堆顶(最大值)与堆尾元素交换,缩小堆范围,再对新堆顶执行 "下沉" 操作(维持堆结构),重复至堆为空。
    • 堆:完全二叉树的一种,可通过数组索引快速访问父 / 子节点(父 i,左子 2i+1,右子 2i+2)。
  • 复杂度

    • 时间:最佳 / 最坏 / 平均 O (n log n)
    • 空间:O (1)(原地排序,堆结构通过数组索引维护)
  • 特点:不稳定排序,适合处理海量数据(堆结构可用于 Top K 问题)。

三、O (n) 时间复杂度的排序算法(非比较排序)

这类算法不通过元素比较实现排序,而是利用 "数值范围" 或 "哈希" 特性,仅适用于特定场景。

1. 计数排序(Counting Sort)
  • 方法

    1. 找出数据的最大值和最小值,创建一个计数数组(长度为 max-min+1),统计每个值出现的次数。
    2. 根据计数数组的累计次数,反向填充原数组(保证稳定性)。
    • 示例:排序 [2, 1, 3, 1, 2],计数数组统计后,按累计次数放回元素。
  • 复杂度

    • 时间:O (n + k)(n 为数据量,k 为数值范围 max-min+1)
    • 空间:O (k)(计数数组开销)
  • 特点:稳定排序,仅适用于数值范围小且为整数的数据(如年龄、成绩)。

2. 桶排序(Bucket Sort)
  • 方法

    1. 将数据分到若干个 "桶" 中(每个桶对应一个范围),桶内数据用其他排序算法(如插入排序)排序。
    2. 按桶的顺序拼接所有桶的结果。
    • 示例:排序 [0.42, 0.32, 0.33, 0.52, 0.37],按 0.1 间隔分桶,每个桶排序后拼接。
  • 复杂度

    • 时间:O (n + k)(n 为数据量,k 为桶数,若数据均匀分布,桶内排序接近 O (1))
    • 空间:O (n + k)(桶的存储空间)
  • 特点:稳定排序,适用于均匀分布的数据(如浮点数、身份证号分段)。

3. 基数排序(Radix Sort)
  • 方法

    1. 按 "低位到高位"(或反之)依次对数据的每一位进行排序(每位用计数排序 / 桶排序)。
    2. 每轮排序后,数据按当前位有序,最终整体有序。
    • 示例:排序 [123, 45, 678],先按个位排序,再按十位,最后按百位。
  • 复杂度

    • 时间:O (d*(n + k))(d 为位数,k 为每一位的基数,如十进制 k=10)
    • 空间:O (n + k)(临时存储桶)
  • 特点:稳定排序,适用于位数固定的数据(如整数、字符串)。

总结:排序算法对比表

排序算法 平均时间复杂度 最坏时间复杂度 空间复杂度 稳定性 适用场景
冒泡排序 O(n²) O(n²) O(1) 稳定 小规模、几乎有序数据
选择排序 O(n²) O(n²) O(1) 不稳定 小规模数据,交换成本高的场景
插入排序 O(n²) O(n²) O(1) 稳定 小规模、部分有序数据
快速排序 O(n log n) O(n²) O(log n) 不稳定 大规模数据(实际应用首选)
归并排序 O(n log n) O(n log n) O(n) 稳定 大规模数据、链表排序
堆排序 O(n log n) O(n log n) O(1) 不稳定 大规模数据,内存有限场景
计数排序 O(n + k) O(n + k) O(k) 稳定 数值范围小的整数
桶排序 O(n + k) O(n²) O(n + k) 稳定 均匀分布的数据
基数排序 O(d*(n + k)) O(d*(n + k)) O(n + k) 稳定 位数固定的数据(整数、字符串)

选择建议

小规模数据:插入排序(最快)。

  • 大规模数据:快速排序(平均最优)、归并排序(稳定)、堆排序(内存紧张)。
  • 特殊场景:数值范围小用计数排序,均匀分布用桶排序,位数固定用基数排序。
相关推荐
THMAIL1 天前
深度学习从入门到精通 - 神经网络核心原理:从生物神经元到数学模型蜕变
人工智能·python·深度学习·神经网络·算法·机器学习·逻辑回归
野犬寒鸦1 天前
力扣hot100:旋转图像(48)(详细图解以及核心思路剖析)
java·数据结构·后端·算法·leetcode
墨染点香1 天前
LeetCode 刷题【61. 旋转链表】
算法·leetcode·职场和发展
一枝小雨1 天前
【OJ】C++ vector类OJ题
数据结构·c++·算法·leetcode·oj题
Tisfy1 天前
LeetCode 3516.找到最近的人:计算绝对值大小
数学·算法·leetcode·题解
自信的小螺丝钉1 天前
Leetcode 206. 反转链表 迭代/递归
算法·leetcode·链表
黑色的山岗在沉睡1 天前
LeetCode 189. 轮转数组
java·算法·leetcode
墨染点香1 天前
LeetCode 刷题【65. 有效数字】
算法·leetcode·职场和发展
源代码•宸1 天前
Leetcode—2749. 得到整数零需要执行的最少操作数【中等】(__builtin_popcountl)
c++·经验分享·算法·leetcode·位运算