算法知识图谱总览 / 排序与基础算法族 / Timsort

Timsort

P3intermediatebriefG-A-1

What · 是什么

归并+插入混合稳定排序,Python/Java 标准库使用

Timsort 是 2002 年 Tim Peters 为 Python 设计的混合稳定排序,结合插入排序与归并排序,并针对『已存在有序段(Run)』做优化:识别 Run → 不足 MIN_RUN 用插入排序扩展 → 用栈+特定归并规则(galloping 加速)合并相邻 Run。已有序数据接近 O(n),最坏 O(n log n),空间 O(n)。是 Python、Java SE7+ 对象数组、GNU Swift 的默认排序。

Why · 为什么

直觉 · Intuition

真实数据常有大量部分有序段。Timsort 先把这些天然有序段找出来(升序直接用、降序反转),再把它们高效合并——就像人类对『大半已排好的牌』补几张牌而不是全盘重排。

原理 · Principle

三个关键设计:①Run 识别:升序段直接取,降序段反转;长度不足 MIN_RUN(32~64,按 n 计算)用二分插入排序扩展。②栈归并规则:压入 Run 后检查栈顶 X,Y,Z 满足 len(Z)>len(Y)+len(X) 且 len(Y)>len(X),否则合并较小的相邻对,保证归并平衡(防斐波那契式退化,见 ESA 2018 最坏情况论文)。③Galloping:当一侧连续赢 ≥Min_Gallop(7) 次就进入加速模式,用指数查找+二分查找批量搬移元素,动态调整 Min_Gallop。

历史 · History

2002 年 Tim Peters 在 Python 2.3 引入,Python 内建 sort 至今用它;2009 年 Java SE 7 用于非原始类型数组;也被 Android、GNU Swift 采用。其最坏情况复杂度直到 2015 年才由 Augusteijn 等给出严格证明(论文《On the Worst-Case Complexity of TimSort》,ESA 2018)。

How · 实现

伪代码

tim_sort(a):
    nRemaining = len(a)
    minRun = compute_min_run(n)          # 32~64
    start = 0
    while nRemaining > 0:
        runLen = 识别天然 Run(不足 minRun 用插入排序补齐到 minRun)
        push (start, runLen) 到栈
        merge_collapse(栈)               # 保证堆叠平衡
        start += runLen; nRemaining -= runLen
    merge_force_collapse(栈)
    return a

Python 实现

MIN_RUN = 32


def binary_insertion_sort(a, lo, hi):
    """二分插入排序区间 [lo, hi),用于补齐小 Run"""
    for i in range(lo + 1, hi):
        key = a[i]
        # 二分找插入点(lo, i] 区间已有序)
        l, r = lo, i
        while l < r:
            m = (l + r) // 2
            if a[m] <= key:
                l = m + 1
            else:
                r = m
        for j in range(i, l, -1):
            a[j] = a[j - 1]
        a[l] = key


def count_run(a, lo):
    """识别从 lo 开始的天然 Run,降序则反转,返回 Run 的结束下标"""
    n = len(a)
    if lo + 1 >= n:
        return lo + 1
    if a[lo] <= a[lo + 1]:           # 升序
        hi = lo + 1
        while hi + 1 < n and a[hi] <= a[hi + 1]:
            hi += 1
    else:                            # 降序 → 反转成升序
        hi = lo + 1
        while hi + 1 < n and a[hi] > a[hi + 1]:
            hi += 1
        a[lo:hi + 1] = a[lo:hi + 1][::-1]
    return hi + 1


def merge_runs(a, lo, mid, hi):
    """合并两个相邻有序段 [lo,mid) 与 [mid,hi)"""
    left = a[lo:mid]
    i, j, k = 0, mid, lo
    while i < len(left) and j < hi:
        if a[j] < left[i]:
            a[k] = a[j]; j += 1
        else:
            a[k] = left[i]; i += 1
        k += 1
    while i < len(left):
        a[k] = left[i]; i += 1; k += 1


def tim_sort(a):
    """简化版 Timsort:识别 Run + 插入补齐 + 归并所有 Run"""
    n = len(a)
    if n <= 1:
        return a
    runs = []
    lo = 0
    while lo < n:
        hi = count_run(a, lo)
        if hi - lo < MIN_RUN:
            hi = min(lo + MIN_RUN, n)
            binary_insertion_sort(a, lo, hi)
        runs.append((lo, hi))
        lo = hi
    while len(runs) > 1:
        new_runs = []
        for k in range(0, len(runs) - 1, 2):
            merge_runs(a, runs[k][0], runs[k][1], runs[k + 1][1])
            new_runs.append((runs[k][0], runs[k + 1][1]))
        if len(runs) % 2:
            new_runs.append(runs[-1])
        runs = new_runs
    return a


if __name__ == "__main__":
    import random
    for _ in range(200):
        arr = [random.randint(0, 200) for _ in range(random.randint(0, 150))]
        b = arr[:]
        tim_sort(b)
        assert b == sorted(arr), (arr, b)
    near_sorted = list(range(5000))
    near_sorted[2500], near_sorted[2501] = near_sorted[2501], near_sorted[2500]
    b = near_sorted[:]
    tim_sort(b)
    assert b == sorted(near_sorted)
    print("简化 Timsort 正确性 OK(200 组随机 + 近乎有序)")
    print("示例:", tim_sort([3, 7, 4, 9, 5, 2, 6, 1]))
PyPy 提示比赛中不需要手写 Timsort——Python 内建 list.sort 就是它。本实现为教学简化版,去掉了 galloping 与栈平衡规则,仅演示 Run 识别+插入补齐+归并骨架。
时间复杂度已有序 O(n),最坏 O(n log n)
空间复杂度O(n)
注意 · 局限真实实现(galloping、mergeCollapse 栈规则、Min_Gallop 动态调整)相当复杂,比赛中应直接用内建 sort;理解其原理有助于写出对有序数据更友好的代码。

易错点

例题

OJ题号难度考点链接
OI-Wikibasic/tim-sort3Timsort 伪代码与归并规则https://oi-wiki.org/basic/tim-sort/
CPythonObjects/listsort.txt3Tim Peters 原始说明https://github.com/python/cpython/blob/main/Objects/listsort.txt
步骤迷你图(节点 = 算法步骤 · 边 = 流程 · 绿色 = 起始步骤)
上游 · 本知识点依赖(前置/变体/组合/共现)

信源

✓ = 已核验 · △ = 未核验