排序是算法世界的地基:几乎所有面试、几乎所有数据处理都绕不开它。本文把最常考的三种排序——冒泡、快排、归并——一次性讲透,包括思路、代码、复杂度,以及"面试官到底想考你什么"。

1. 为什么要学排序

排序的价值远超"把数组排整齐":有序后配合二分查找,复杂度从 O(n) 降到 O(log n);求中位数、合并区间、去重也都要先排序。更重要的是,排序是练习分治与双指针思想的绝佳素材。

2. 冒泡排序:最直观的入门

核心思想:相邻元素两两比较,大的往后冒,每一轮过后最大的元素"浮"到末尾。

def bubble_sort(arr):
    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

print(bubble_sort([5, 2, 9, 1, 5, 6]))  # [1, 2, 5, 5, 6, 9]

swapped 标志后,已有序时一轮结束,最好情况 O(n);平均与最坏仍为 O(n²)。

3. 快速排序:平均最快的通用排序

快排是分治的典型:选一个基准(pivot),小于它的放左边、大于它的放右边,再递归处理两侧。

def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    pivot = arr[len(arr) // 2]
    left  = [x for x in arr if x < pivot]
    mid   = [x for x in arr if x == pivot]
    right = [x for x in arr if x > pivot]
    return quick_sort(left) + mid + quick_sort(right)

print(quick_sort([3, 6, 8, 10, 1, 2, 1]))  # [1, 1, 2, 3, 6, 8, 10]

平均 O(n log n),空间 O(log n)(递归栈)。若基准总选到极值,最坏退化为 O(n²),工业实现常用"三数取中"规避。

4. 归并排序:稳定且复杂度稳定

归并排序先不断二分到单元素,再两两合并有序子数组。无论好坏都是 O(n log n),且是稳定排序

def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    mid = len(arr) // 2
    return merge(merge_sort(arr[:mid]), merge_sort(arr[mid:]))

def merge(a, b):
    i = j = 0
    res = []
    while i < len(a) and j < len(b):
        if a[i] <= b[j]:
            res.append(a[i]); i += 1
        else:
            res.append(b[j]); j += 1
    return res + a[i:] + b[j:]

print(merge_sort([38, 27, 43, 3, 9, 82, 10]))  # [3, 9, 10, 27, 38, 43, 82]

代价是需要 O(n) 额外空间合并,内存极紧时不适用。

5. 三种排序一表对比

算法平均时间最坏时间空间稳定性
冒泡排序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)稳定

6. 面试怎么答

💡 学习建议:排序算法光看没用,建议用纸笔手动模拟一轮快排的交换过程,再在白板上默写三种排序,直到一次通过为止。