【Python LeetCode 专题】排序算法

  • 十大排序算法
    • [比较类排序(Comparison Sort)](#比较类排序(Comparison Sort))
      • [快速排序(Quick Sort)](#快速排序(Quick Sort))
      • [归并排序(Merge Sort)](#归并排序(Merge Sort))
      • [堆排序(Heap Sort)](#堆排序(Heap Sort))
      • [希尔排序(Shell Sort)](#希尔排序(Shell Sort))
      • [插入排序(Insertion Sort)](#插入排序(Insertion Sort))
      • [冒泡排序(Bubble Sort)](#冒泡排序(Bubble Sort))
      • [选择排序(Selection Sort)](#选择排序(Selection Sort))
    • [2. 非比较类排序(Non-Comparison Sort)](#2. 非比较类排序(Non-Comparison Sort))

十大排序算法

虽然 sorted() 函数可以完成排序任务,但学习 十大经典排序算法 仍然很有价值。

  1. 深入理解排序原理
    sorted() 只是 Python 提供的工具,本质上它使用的是 Timsort(归并 + 插入排序) 。学习排序算法可以帮助理解 不同算法的时间复杂度、空间复杂度和适用场景 ,能根据需求选择合适的排序方法。如果不了解排序算法的原理,在面对不同场景时,可能会做出低效或错误的选择。例如:
    • 为什么 Python 选择 Timsort?
    • 为什么快速排序在大多数情况下比归并排序更快?
    • 为什么插入排序在数据基本有序时反而更高效?
  2. 处理特殊需求
    有些情况下,内置 sorted() 并不能满足所有需求,而自定义排序算法可以提供更好的优化。例如:
    • 大数据排序(外部排序):在数据量超大,无法全部放入内存时,可能需要归并排序或堆排序。
    • 稳定排序 vs. 不稳定排序sorted() 是稳定排序,但如果不需要稳定性,可以用更快的不稳定排序(如快速排序)。
    • 多关键字排序:需要按照多个字段进行排序时,可能需要定制化的比较规则。

参考:十大经典排序算法 动图Sorting Algorithms

简单理解:

  • 比较排序:靠元素间 "比大小" 排序

  • 非比较排序:靠 "特征" 直接定位

  • 稳定排序(Stable Sorting):排序后 相等的元素 仍然 保持它们原本的相对顺序,则称该排序算法是稳定的。

  • 不稳定排序(Unstable Sorting):排序后 相等的元素相对位置可能发生变化,则该算法是不稳定的。

  • 内部排序(Internal Sorting) :如果 排序完全在内存中完成,不涉及外部存储(如磁盘、数据库等),则称为内部排序。

  • 外部排序(External Sorting) :如果数据量太大,无法一次性放入内存,需要 借助磁盘、数据库等存储来进行排序,则称为外部排序。

比较类排序(Comparison Sort)

排序算法 平均时间复杂度 最好情况 最坏情况 空间复杂度 排序方式 稳定性
快速排序 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) 内部排序 不稳定
插入排序 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) 的时间复杂度下界 (基于 决策树模型 的理论下界)。
  • 适用于所有数据类型(不论是整数、字符串还是对象,只要支持比较操作)。

快速排序(Quick Sort)

快速排序通常是处理 大规模数据排序 时的 首选算法之一,尤其是在数组数据结构中。通过合理选择基准元素和优化递归过程,快速排序可以非常高效。

快速排序是一种基于 分治法(Divide and Conquer) 的排序算法,

  • 从数列中挑出一个元素,称为 "基准"(pivot
  • 重新排序数列,比基准值小的放在基准前面,比基准值大的放在基准后面 (相同的数可以到任一边)。在这个分区退出之后,该 基准就处于数列的中间位置 。这个称为 分区(partition)操作
  • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
python 复制代码
def quick_sort(arr):
    # 基本情况:数组长度为0或1时,已经有序
    if len(arr) <= 1:
        return arr
    
    # 选择基准元素,通常选择第一个元素作为基准
    pivot = arr[0]
    
    # 分割数组:小于等于基准元素的部分,和大于基准元素的部分
    left = [x for x in arr[1:] if x <= pivot]
    right = [x for x in arr[1:] if x > pivot]
    
    # 递归排序并合并结果
    return quick_sort(left) + [pivot] + quick_sort(right)


nums = [5, 3, 8, 4, 2]
sorted_nums = quick_sort(nums)
print("排序后:", sorted_nums)  # [2, 3, 4, 5, 8]

归并排序(Merge Sort)

归并排序是一种 **分治法(Divide and Conquer)**的排序算法。

核心思想是:将大问题 分解 成小问题,递归 地解决小问题,再将小问题的解决方案 合并 起来。

python 复制代码
def merge_sort(arr):
    if len(arr) <= 1:
        return arr  # 基本情况:数组长度小于等于1时已经有序
    
    # 将数组从中间分割成两半
    mid = len(arr) // 2
    left_half = arr[:mid]
    right_half = arr[mid:]
    
    # 递归排序两半
    left_sorted = merge_sort(left_half)
    right_sorted = merge_sort(right_half)
    
    # 合并两半有序数组
    return merge(left_sorted, right_sorted)

def merge(left, right):
    result = []
    i = j = 0
    # 比较左右两个数组的元素,按顺序放入 result 中
    while i < len(left) and j < len(right):
        if left[i] < right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
   
    # 将剩余的元素加入 result 中
    result.extend(left[i:])
    result.extend(right[j:])
    return result


nums = [5, 3, 8, 4, 2]
sorted_nums = merge_sort(nums)
print("排序后:", sorted_nums)  #  [2, 3, 4, 5, 8]

堆排序(Heap Sort)

堆排序是一种基于 堆数据结构 的排序算法。堆是一种 完全二叉树,其中满足特定的顺序性质:

  • 大根堆(Max Heap):每个父节点的值都大于或等于其子节点的值。
  • 小根堆(Min Heap):每个父节点的值都小于或等于其子节点的值。

堆排序的核心思想是:

  • 构建堆:将待排序的数组转换成一个堆结构。
  • 堆化:将堆顶元素(最大值或最小值)和堆底元素交换,然后对剩下的堆进行调整(维护堆的性质)。
  • 递归调整堆:重复上一步骤,直到堆的大小为 1,即数组已经排好序。

希尔排序(Shell Sort)

希尔排序是 插入排序的一种改进版本 ,它通过将数据 分为多个子序列 来减少元素的移动次数。具体来说,希尔排序会使用一个称为 "增量" 的参数,首先 将数据按增量分组 进行插入排序,然后 逐渐减小增量,直到增量为 1,此时的插入排序会更加高效。

希尔排序的核心思想是:通过 分组 来减少元素的移动,改进插入排序的效率。

python 复制代码
def shell_sort(array):
    n = len(array)
    gap = n // 2  # 增量
    while gap > 0:
        for i in range(gap, n):
            for j in range(i, gap-1, -gap):
                if array[j] < array[j-gap]:
                    array[j], array[j-gap] = array[j-gap], array[j]
                else:
                    break
        gap //= 2   # 减小增量
    return array


nums = [5, 3, 8, 4, 2]
sorted_nums = shell_sort(nums)
print("排序后:", sorted_nums)  # [2, 3, 4, 5, 8]

插入排序(Insertion Sort)

插入排序的思想,就像 打扑克牌理牌 的过程。你手上的牌 从左往右插入,让它们逐步保持有序。

每次从 未排序的元素中取出一个插入到前面已排好序的位置中

python 复制代码
def insertion_sort(arr):
    for i in range(1, len(arr)):
        key = arr[i]  # 当前待插入的元素
        j = i - 1
        # 向前查找合适的位置
        while j >= 0 and arr[j] > key:
            arr[j + 1] = arr[j]  # 元素右移
            j -= 1
        arr[j + 1] = key  # 插入到正确位置
    return arr


nums = [5, 3, 8, 4, 2]
sorted_nums = insertion_sort(nums)
print("排序后:", sorted_nums)  # [2, 3, 4, 5, 8]

冒泡排序(Bubble Sort)

正如图中所示,重复地 比较相邻的两个元素 (绿色),如果它们的顺序错误就把它们交换 过来。大的元素会像"泡泡"一样一步步"冒"到最后(黄色)。

python 复制代码
def bubble_sort(arr):
    n = len(arr)
    for i in range(n):  # 总共需要进行 n 次遍历
        for j in range(0, n - 1 - i):  # 每次减少已排好的元素
            if arr[j] > arr[j + 1]:  # 相邻元素比较
                arr[j], arr[j + 1] = arr[j + 1], arr[j]  # 交换位置
    return arr


nums = [5, 3, 8, 4, 2]
sorted_nums = bubble_sort(nums)
print("排序后:", sorted_nums)  # [2, 3, 4, 5, 8]

【优化】可以加一个标志变量 swapped,如果 某一轮没有发生交换,说明已经有序了,提前结束排序

python 复制代码
def bubble_sort_optimized(arr):
    n = len(arr)
    for i in range(n):
        swapped = False
        for j in range(0, n - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                swapped = True
        if not swapped:  # 没有交换,提前 return
            break
    return arr

选择排序(Selection Sort)

简单直观:每一轮从 未排序 的元素中 "选择"一个最小的(或最大的) ,然后 放到已排序序列的末尾

python 复制代码
def selection_sort(arr):
    n = len(arr)
    for i in range(n):
        min_index = i  # 假设当前位置 i 是最小值
        for j in range(i + 1, n):
            if arr[j] < arr[min_index]:
                min_index = j  # 找到更小的元素索引
        arr[i], arr[min_index] = arr[min_index], arr[i]  # 交换位置
    return arr


nums = [5, 3, 8, 4, 2]
sorted_nums = selection_sort(nums)
print("排序后:", sorted_nums)  # [2, 3, 4, 5, 8]

2. 非比较类排序(Non-Comparison Sort)

排序算法 平均时间复杂度 最好情况 最坏情况 空间复杂度 排序方式 稳定性
计数排序 O(n + k) O(n + k) O(n + k) O(k) 外部排序 稳定
桶排序 O(n + k) O(n + k) O(n²) O(n + k) 外部排序 稳定
基数排序 O(n × k) O(n × k) O(n × k) O(n + k) 外部排序 稳定

特点:

  • 不依赖元素的比较,而是利用元素的某些特征(如数值范围、位数等)进行排序
  • 有可能突破 O(n log n) 的时间复杂度,但通常会占用额外的存储空间。
  • 数据有特定要求 ,通常仅适用于整数、字符串等可进行"直接定位"的数据

计数排序

桶排序

基数排序

相关推荐
胡耀超6 分钟前
图像颜色理论与数据挖掘应用的全景解析
人工智能·python·opencv·计算机视觉·数据挖掘·视觉检测·pillow
love530love21 分钟前
家用或办公 Windows 电脑玩人工智能开源项目配备核显的必要性(含 NPU 及显卡类型补充)
人工智能·windows·python·开源·电脑
学习中的码虫41 分钟前
数据结构中的高级排序算法
数据结构·算法·排序算法
chilavert3181 小时前
关于Python 实现接口安全防护:限流、熔断降级与认证授权的深度实践
python·网络安全
能来帮帮蒟蒻吗2 小时前
Python -将MP4文件转为GIF图片
开发语言·python·学习·视频
suoxiao7772 小时前
通过anaconda安装jupyter
ide·python·jupyter
百锦再2 小时前
MK米客方德SD NAND:无人机存储的高效解决方案
人工智能·python·django·sqlite·android studio·无人机·数据库开发
PacosonSWJTU2 小时前
python使用matplotlib画图
开发语言·python·matplotlib
伶俐角少儿编程2 小时前
2023年12月中国电子学会青少年软件编程(Python)等级考试试卷(六级)答案 + 解析
python·青少年编程·少儿编程·中国电子学会等级考试·中国电子学会
tangjunjun-owen2 小时前
Milvus 2.4 使用详解:从零构建向量数据库并实现搜索功能(Python 实战)
数据库·python·milvus·rag