Skip to content

3.1 排序的理论极限

这一节,你能解决什么问题

学完这一节,你能够:

  1. 证明基于比较的排序有 Ω(n log n) 下界,不是靠"感觉",而是用决策树模型和信息论
  2. 判断什么时候可以突破这个下界,知道计数排序的前提条件
  3. 审查 agent 给出的"优化排序"建议,发现隐藏的前提条件错误

问题情境

某推荐团队要优化排序服务。系统对 10^8 条数据排序,耗时约 2.6 秒。

工程师小李问 Cursor:"有没有比快排更快的排序?"

Cursor 回答:"计数排序 O(n),比快排 O(n log n) 快多了。"

小李照做,测试通过,上线——系统崩溃。

问题在哪里?

Cursor 不知道前提条件。小李没有验证。

计数排序的前提是:键值范围 k ≤ n 或 k = O(n)。

推荐系统排序的键是浮点数相关性得分,范围远超 n。

教训:知道算法的理论极限和前提条件,才能判断 agent 的建议是否可行。


直观思路

先思考一个问题:为什么排序"至少需要"这么多比较?

假设你要猜一个数字,范围 1 到 100。

每次你问"比 X 大吗",只能得到"是"或"否"。

最少需要问几次才能保证猜到?

答案是 lg(100) ≈ 7 次。

为什么?因为每次回答最多给你 1 bit 信息。要从 100 个可能中确定一个,需要 lg(100) bit 信息。

排序也是类似的问题

  • n 个元素有 n! 种可能排列
  • 每次比较最多提供 1 bit 信息
  • 需要至少 lg(n!) bit 才能确定排列

规范定义

决策树模型

把排序的所有可能比较过程画出来。

每次比较得到"是"或"否",形成一棵二叉树:

排序三个元素 [a, b, c]:

              a ≤ b?
             /      \
           是        否
          /            \
       b ≤ c?         a ≤ c?
      /     \        /      \
    是       否     是        否
   /          \    /          \
[a,b,c]    a ≤ c?           b ≤ a?
          /      \          /      \
        是        否       是        否
       /          \       /          \
  [a,c,b]       b ≤ a?  [b,c,a]    [b,a,c]
               /    \
             是      否
            /          \
       [c,a,b]      [c,b,a]

定义

  • 内部节点:一次比较操作
  • 边:比较结果
  • 叶子:一种排列结果
  • 树高度:最坏情况需要的比较次数

下界定义

任何基于比较的排序算法,最坏情况下至少需要 Ω(n log n) 次比较。


算法实现

这不是一个具体算法,而是所有比较排序的下界证明。

但理解下界,能帮你判断 agent 的建议是否可行。


这个方法是怎么想到的

你可能会问:为什么用决策树来证明下界?

让我们看看思考过程。

思路一:直接计算

朴素想法:每次比较减少不确定性,需要多少次才能确定排列?

问题:这个想法太抽象,没有具体的模型。

思路二:画决策树

发现:每次比较只有两种结果,形成二叉树结构。

关键:从根到叶子的路径长度,就是比较次数。

思路三:信息论角度

洞察:n! 种排列需要 n! 个叶子。

高度 h 的二叉树最多有 2^h 个叶子。

所以:h ≥ lg(n!) = Ω(n log n)。


正确性分析

下界证明:四步

Step 1:决策树必须有 n! 个叶子

为什么?n 个元素有 n! 种排列。少一片叶子,就有一个输入无法正确排序。

Step 2:高度 h 的二叉树最多 2^h 个叶子

归纳法:

  • h = 0:只有根节点,1 = 2^0 个叶子 ✓
  • h = k+1:两个子树各有最多 2^k 个叶子,总共 2^{k+1} ✓

Step 3:合并不等式

n! ≤ 叶子数 ≤ 2^h
因此 h ≥ lg(n!)

Step 4:Stirling 近似

n! ≈ (n/e)^n × √(2πn)

lg(n!) = n lg n - n lg e + Θ(lg n)
       = n lg n - Θ(n)
       = Ω(n lg n)

结论:基于比较的排序,最坏情况至少 Ω(n lg n)。


复杂度分析

下界是理论极限

这不是某个算法的限制,而是比较方法本身的限制。

为什么归并排序和堆排序达到下界?

因为它们是 Θ(n lg n),正好等于下界——渐近最优。

为什么快排没有达到下界?

因为快排最坏 Θ(n²),但期望 Θ(n lg n)。期望情况下渐近最优。


什么时候不该用这个方法

该突破下界的情况

下界只对"比较排序"成立。如果可以利用额外信息,就能突破:

算法时间突破方式前提条件
计数排序Θ(n + k)直接定位键是整数,范围 k ≤ n
基数排序Θ(d(n + k))逐位排序键可分解为 d 位
桶排序Θ(n) 期望概率分布输入均匀分布

突破的关键:利用额外信息(键值范围、键的结构、输入分布)。

不该突破的情况

如果没有额外信息(键是任意浮点数、字符串),就必须用比较排序。

排序三元悖:不存在同时满足三者的算法:

  1. O(n) 时间
  2. 稳定
  3. 原地
算法时间稳定原地缺少哪个?
计数排序Θ(n + k)不原地
归并排序Θ(n lg n)不原地
堆排序Θ(n lg n)不稳定
快排Θ(n lg n) 期望不稳定 + 最坏Θ(n²)

本节小结

解决了什么问题?

  • 证明比较排序的理论极限 Ω(n lg n)
  • 知道什么时候可以突破,什么时候不能

核心方法是什么?

  • 决策树模型:把比较过程画成树,树高度就是比较次数
  • 信息论视角:每次比较最多 1 bit 信息,n! 种排列需要 lg(n!) bit

为什么正确?

决策树必须覆盖所有排列 → 需要 n! 个叶子 → 高度 ≥ lg(n!) = Ω(n lg n)。

代价是什么?

这是理论下界,不是具体算法的代价。

突破下界的代价是:额外信息(键值范围、键结构、输入分布)。

适用场景?

下界告诉你要权衡:

  • 有额外信息 → 可以突破
  • 没有额外信息 → 必须用 Θ(n lg n) 排序
  • 需要稳定 + 原地 → 必须接受 Θ(n lg n)

练习

基本理解

练习 1:用决策树模型证明:排序 3 个元素,最坏情况至少需要 5 次比较。

练习 2:对 n = 1000,计算排序下界 lg(1000!),并用 Stirling 近似验证。

练习 3:解释为什么归并排序是渐近最优的比较排序。

方法应用

练习 4:给出以下场景,判断能否用计数排序突破 Θ(n lg n):

场景键类型键值范围n能否用计数排序?
年龄排序整数0-15010^6
成绩排序整数0-10010^5
ID 排序整数1-10^910^5
价格排序浮点数0-100010^6

错误诊断

练习 5:以下 agent 建议有什么问题?

"用计数排序优化日志系统的时间戳排序,比快排快 O(n)。"

分析:时间戳的键值范围是多少?n 是多少?是否满足前提?

方案比较

练习 6:分析排序三元悖。给出一个需要"稳定 + 原地"的场景,说明唯一选择是什么(时间代价)。

开放设计

练习 7:设计一个"排序选择决策树"。

给定输入特征:

  • 键类型(整数/浮点/字符串)
  • 键值范围 k
  • 数据规模 n
  • 是否需要稳定
  • 是否需要原地

输出推荐算法和理由。


下一节:3.2 插入排序

新时代的算法课程