Skip to content

排序算法

本章目标:全面掌握比较类和非比较类排序算法,理解各算法的适用场景。

你将学到

  • 排序的稳定性、原地性
  • O(n²) 三兄弟:冒泡、选择、插入
  • O(n log n) 三大将:归并、快排、堆排
  • 非比较排序:计数、基数、桶排
  • 各排序算法的对比与选择

1. 概念

排序是计算机科学中最基本也最重要的操作之一。理解排序算法不仅是面试基础,更能帮助理解分治、递归、堆等重要概念。

稳定性:相等元素排序后是否保持原来相对顺序。稳定排序在多关键字排序中有用(如先按年龄排,再按姓名稳定排,同龄人保持年龄排序的顺序)。

原地排序:是否只需 O(1) 额外空间。

排序算法对比

算法平均时间最坏时间空间稳定原地
冒泡O(n²)O(n²)O(1)
选择O(n²)O(n²)O(1)
插入O(n²)O(n²)O(1)
归并O(n log n)O(n log n)O(n)
快排O(n log n)O(n²)O(log n)
堆排O(n log n)O(n log n)O(1)
计数O(n+k)O(n+k)O(k)
基数O(d·n)O(d·n)O(n+d)
桶排O(n+k)O(n²)O(n+k)

2. 核心实现

冒泡排序

python
def bubble_sort(arr):
    """稳定,O(n²),适合小规模或近乎有序数据"""
    n = len(arr)
    for i in range(n - 1):
        swapped = False
        for j in range(n - 1 - i):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                swapped = True
        if not swapped:  # 优化:无交换说明已有序
            break
    return arr

插入排序

python
def insertion_sort(arr):
    """稳定,O(n²),小数据量或近乎有序时优于快排"""
    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

归并排序

python
def merge_sort(arr):
    """稳定,O(n log n),需要 O(n) 额外空间"""
    if len(arr) <= 1:
        return arr
    mid = len(arr) // 2
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    return merge(left, right)

def merge(left, right):
    result = []
    i = j = 0
    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.extend(left[i:])
    result.extend(right[j:])
    return result

快速排序

python
import random

def quick_sort(arr, lo, hi):
    """不稳定,平均 O(n log n),原地"""
    if lo >= hi:
        return
    pivot_idx = random.randint(lo, hi)
    arr[lo], arr[pivot_idx] = arr[pivot_idx], arr[lo]
    pivot = arr[lo]
    i, j = lo, hi
    while i < j:
        while i < j and arr[j] >= pivot:
            j -= 1
        arr[i] = arr[j]
        while i < j and arr[i] <= pivot:
            i += 1
        arr[j] = arr[i]
    arr[i] = pivot
    quick_sort(arr, lo, i - 1)
    quick_sort(arr, i + 1, hi)

堆排序

python
def heap_sort(arr):
    """不稳定,O(n log n),原地"""
    n = len(arr)
    # 建大根堆
    for i in range(n // 2 - 1, -1, -1):
        heapify(arr, n, i)
    # 逐个取出最大值
    for i in range(n - 1, 0, -1):
        arr[0], arr[i] = arr[i], arr[0]
        heapify(arr, i, 0)

def heapify(arr, n, i):
    largest = i
    left, right = 2 * i + 1, 2 * i + 2
    if left < n and arr[left] > arr[largest]:
        largest = left
    if right < n and arr[right] > arr[largest]:
        largest = right
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]
        heapify(arr, n, largest)

计数排序

python
def counting_sort(arr, max_val):
    """稳定,O(n+k),适合值域小的非负整数"""
    count = [0] * (max_val + 1)
    for num in arr:
        count[num] += 1
    result = []
    for num in range(max_val + 1):
        result.extend([num] * count[num])
    return result

3. 实际应用选择

  • 小数据量(n < 50):插入排序(TimSort 内部也用它)
  • 通用排序:快排(Python/Java 默认对基本类型用快排变体)
  • 需要稳定排序:归并排序(TimSort 是归并+插入的混合)
  • 值域小:计数排序
  • 大数据外部排序:归并排序(可分块读取)

Python 的 sort() 使用 TimSort(归并+插入),最坏 O(n log n),在部分有序数据上接近 O(n)。

4. 易错点

  • ⚠️ 快排选择第一个元素做 pivot 在已排序数据上退化到 O(n²)
  • ⚠️ 归并排序的 merge<= 保证稳定性
  • ⚠️ 计数排序只适用于非负整数(或可映射到非负)
  • ⚠️ Python 递归深度限制约 1000,大规模数据快排需要手动提高限制或改为迭代

5. 推荐刷题清单

题号题名难度关键
912排序数组Medium实现各排序
169多数元素Easy排序后取中间
215第 K 个最大元素Medium快速选择
56合并区间Medium按左端点排序
179最大数Medium自定义排序
252会议室Easy按开始时间排序

延伸阅读

基于 VitePress 构建 · 部署于 Cloudflare Pages