第 9 章 中位数与顺序统计量 (Medians and Order Statistics)

      +

      核心结论

      • 顺序统计量 (Order Statistic):第 \(i\) 小的元素。中位数是特例:奇数 \(n\) 时为第 \((n+1)/2\) 小;偶数时分下/上中位数,本书默认用下中位数(第 \(\lfloor (n+1)/2 \rfloor\) 小)。

      • 最小/最大值:MINIMUM / MAXIMUM 算法用 \(n-1\) 次比较——这已是最优下界(每个非最小值至少输一次比较)。

      • 同时找最小与最大:用「成对比较」只需 \(\lfloor 3n/2 \rfloor\) 次比较,比朴素的 \(2n-2\) 省 25%;下界也是 \(\lfloor 3n/2 \rfloor - 2\)(练习 9.1-2)。

      • 期望线性选择 (RANDOMIZED-SELECT):仿照快速排序的 PARTITION 但只递归一边;期望时间 \(\Theta(n)\),最坏 \(\Theta(n^2)\)(连续选最差 pivot)。

      • 最坏线性选择 (SELECT, 中位数之中位数算法):分组(5 个一组)→ 求每组中位数 → 再求这些中位数的中位数作为 pivot → 划分递归;保证「每边至少去掉 30%」→ 最坏 \(\Theta(n)\),但常数因子大。

      • 算法选择权衡:期望线性 vs 最坏线性——实践中常用 RANDOMIZED-SELECT(实现简单、常数小);SELECT 用于实时 / 安全场景(无最坏)。

      本章主旨

      本章解决「在 \(n\) 个元素中找第 \(i\) 小的元素」的问题。直接的解法是排序后取第 \(i\) 位,时间 \(O(n \lg n)\)——但对选择问题这是过度的,因为不需要全部顺序。本章给出三个更快的算法:(1) §9.1 极简情形(最小 / 最大 / 同时);(2) §9.2 RANDOMIZED-SELECT(仿快速排序,期望线性);(3) §9.3 SELECT(中位数之中位数,最坏线性,理论意义重大)。本章也是「分治」与「递归式(带单调性假设)」的精彩应用。

      一、核心概念

      本章围绕 5 个核心概念展开:从最小 / 最大这种极简情形引入「比较次数」的下界论证,再给出两种线性时间选择算法——一种期望线性(随机化)、一种最坏线性(确定性中位数之中位数)。

      概念 定义 + 重要性 实现提示

      顺序统计量与中位数

      第 \(i\) 顺序统计量 = 第 \(i\) 小元素;中位数 = 第 \(\lfloor (n+1)/2 \rfloor\) 小(下中位数)。

      §9.1;选择问题:输入是集合 \(A\) + 整数 \(i\),输出是 \(A\) 中恰好有 \(i-1\) 个元素比它小的元素。

      最小/最大比较下界 (n-1)

      任何算法确定最小值至少需要 \(n-1\) 次比较——每个非最小值必须输一次「淘汰赛」。

      §9.1;MINIMUM 用 \(n-1\) 次;同理 MAXIMUM 用 \(n-1\) 次。

      同时最小 + 最大 (\lfloor 3n/2 \rfloor)

      把元素两两配对比较 → 小者比当前最小、大者比当前最大;3 次比较处理 2 个元素。

      §9.1;n 奇 vs 偶初始化细节不同;下界也是 \(\lfloor 3n/2 \rfloor - 2\)。

      期望线性 RANDOMIZED-SELECT

      仿快速排序的 PARTITION,但只递归包含第 \(i\) 小的一边;期望 \(\Theta(n)\),最坏 \(\Theta(n^2)\)。

      §9.2;随机选 pivot 类似第 7 章;用 max(k-1, n-k) 上界 + 代入法证明期望线性。

      中位数之中位数 SELECT (中位五)

      分组(5 个一组)→ 求每组中位数 → 求这些中位数的中位数作为 pivot;保证每边至少去掉 30%。

      §9.3;5 不是魔法数——7 也行(练习 9.3-1),3 不行(递归式不收缩)。

      二、详细笔记

      9.1 最小值、最大值与同时求解

      What:在最简单情形下找顺序统计量——最小值(第 1 顺序统计量)、最大值(第 n 顺序统计量)、或两者同时。

      Why:用极端情形热身 + 建立「比较次数下界」的论证模式(淘汰赛 / 候选集合计数);同时给出实际中常用的「成对比较」优化技巧。

      How

      1. MINIMUM 算法(§9.1):

        \[\begin{array}{rl} 1 & \min = A[1] \\ 2 & \textbf{for } i = 2 \textbf{ to } A.\text{length} \\ 3 & \quad \textbf{if } \min > A[i] \\ 4 & \quad\quad \min = A[i] \\ 5 & \textbf{return } \min \end{array}\]

        比较次数 = \(n-1\)(每次维护当前最小)。

      2. 下界论证(§9.1):把算法执行看作「淘汰赛」——每次比较输者必不可能是最小;除赢家外每个元素必须输至少一次,故至少 \(n-1\) 次比较。

      3. 同时最小 + 最大(§9.1):把元素两两配对。

        • 先对每对做 1 次比较。

        • 再比较小者与当前最小、大者与当前最大 → 3 次比较处理 2 个元素。

        • 总比较次数 = \(\lfloor 3n/2 \rfloor\)(n 偶数:\(1 + 3(n-2)/2 = 3n/2 - 2\);n 奇数:\(3\lfloor n/2 \rfloor\))。

        • 比朴素的「分别求 min 和 max 用 \(2n-2\) 次」省约 25%。

      When

      1. 求一个最小 / 最大 → MINIMUM/MAXIMUM(\(n-1\) 次)。

      2. 同时求两个 → 成对比较(\(\lfloor 3n/2 \rfloor\) 次)。

      3. 「找次小元素」 → 锦标赛法:\(n + \lceil \lg n \rceil - 2\) 次比较(练习 9.1-1)。

      Example:成对比较在 n = 8 个元素的例子: * 配对:(4, 2) → 2, (7, 3) → 3, (9, 1) → 1, (6, 5) → 5。 * 当前最小候选 = ⟨2, 3, 1, 5⟩;当前最大候选 = ⟨4, 7, 9, 6⟩。 * 分别求最小(用 3 次)和最大(用 3 次),外加 4 次配对 → 总 \(3 + 3 + 4 = 10 = 3 \cdot 8 / 2 - 2\)。

      9.2 期望线性时间选择 (RANDOMIZED-SELECT)

      What:仿照 RANDOMIZED-QUICKSORT,用 RANDOMIZED-PARTITION 划分输入;但与快速排序不同的是,只递归包含第 \(i\) 小元素的那一边,另一边丢弃。

      Why:选择问题不需要排序——只关心一个元素。这丢弃另一半的「信息」换取效率。期望 \(\Theta(n)\),因为每次递归至少把问题规模砍掉一半(期望)。

      How

      1. 伪代码(RANDOMIZED-SELECT, §9.2):

        \[\begin{array}{rl} 1 & \textbf{if } p == r \\ 2 & \quad \textbf{return } A[p] \\ 3 & q = \text{RANDOMIZED-PARTITION}(A, p, r) \\ 4 & k = q - p + 1 \\ 5 & \textbf{if } i == k \quad \text{// pivot 是答案} \\ 6 & \quad \textbf{return } A[q] \\ 7 & \textbf{elseif } i < k \\ 8 & \quad \textbf{return } \text{RANDOMIZED-SELECT}(A, p, q-1, i) \\ 9 & \textbf{else } \textbf{return } \text{RANDOMIZED-SELECT}(A, q+1, r, i-k) \end{array}\]

        关键点:

        • 第 4 行:k = 「pivot 在 A[p..r] 中是第几小」。

        • 第 8 行:第 i 小在低侧 → 递归找低侧的第 i 小。

        • 第 9 行:第 i 小在高侧 → 递归找高侧的第 (i-k) 小(因为已有 k 个元素被「跳过」)。

      2. 期望分析(§9.2):

        • 设指示变量 \(X_k = I\{递归调用时低侧有 k 个元素\}\),\(E[X_k\) = 1/n]。

        • 假设递归调用总在较大侧(这是上界),递归式:

          \[E[T(n)] \leq \frac{2}{n} \sum_{k=\lfloor n/2 \rfloor}^{n-1} E[T(k)] + O(n)\]
        • 代入法证明 \(E[T(n)\) \leq cn]:代入后需要 \(-cn/4 + c/2 + an \geq 0\),即 \(n \geq 2c/(c/4 - a)\),对足够大 \(n\) 成立。

      3. 最坏情形 \(\Theta(n^2)\):每次都选最差 pivot(最大或最小)→ 递归式 \(T(n) = T(n-1) + \Theta(n)\) → 求和得 \(\Theta(n^2)\)。

      When

      1. 「实现简单 + 实践中足够快」 → 用 RANDOMIZED-SELECT;实践中常数因子比 SELECT 小。

      2. 「无最坏性能保证就不可接受」(实时系统、安全关键) → 用 SELECT (§9.3)。

      3. 「需要找附近元素」(如排序后取子集)→ RANDOMIZED-SELECT 配合 PARTITION 一次定位。

      Example:在 A = ⟨3, 2, 9, 0, 7, 5, 4, 8, 6, 1⟩ 上找第 5 小(期望): * Pivot = 某随机元素;假设首次 Partition 得低侧大小 k = 5,则 pivot 恰是第 5 小——直接返回。 * 若 k = 6(pivot 是第 6 小),则找高侧的第 (5 - 6) < 0?—— 不会发生(练习 9.2-1 证明不会出现递归到大小 0 的数组)。

      9.3 最坏线性时间选择 (SELECT)

      What:确定性、分治的「中位数之中位数」算法——保证每次划分至少砍掉 30% 的元素,从而得到最坏 \(\Theta(n)\) 时间。

      Why:第 8 章证明排序下界 \(\Omega(n \lg n)\);但选择问题下界是 \(\Omega(n)\)(因为排序能解决选择但不是反之)——理论上能达到下界。本节给出达到下界的算法,理论意义重大。

      How

      1. 算法步骤(SELECT, §9.3):

      2. 分 5 个一组:\(\lceil n/5 \rceil\) 组,每组 5 个,至多一组 less than 5。

      3. 组内中位数:用插入排序对每组排序(O(1) 时间),取中位数;得 \(\lceil n/5 \rceil\) 个中位数。

      4. 递归求中位数的中位数 x:用 SELECT 自身求这些中位数的中位数。

      5. 用 x 划分原数组:得低侧(\(< x\))和高侧(\(> x\)),元素数分别为 \(k - 1\) 和 \(n - k\)(x 是第 k 小)。

      6. 递归或返回:若 i = k 返回 x;若 i < k 对低侧递归;若 i > k 对高侧递归找第 \(i - k\) 小。

      7. 最坏情况分析(§9.3):

        • 关键引理(§9.3,图 9.1):至少 \(3n/10 - 6\) 个元素大于 x,至少 \(3n/10 - 6\) 个元素小于 x。

        • 直观:5 个一组的中位数里,至少一半 ≥ x(除含 x 自身和最后一组);这一半所在的整组中,至少 3 个元素严格 > x。

        • 故递归式:

          \[T(n) \leq \begin{cases} O(1) & \text{if } n < 140 \\ T(\lceil n/5 \rceil) + T(7n/10 + 6) + O(n) & \text{if } n \geq 140 \end{cases}\]
        • 代入 \(T(n) \leq cn\),得 \(cn/5 + 7cn/10 + 7c + an \leq cn\)(需 \(c \geq 20a\)、\(n \geq 140\) 即可)。

      8. 练习 9.3-1(分组大小的影响):

        • 分组 7:每组中位数 ≥/≤ 中位数之中位数的元素数至少 4 个(\(4n/14 - 4\) 个),递归式 \(T(n/7) + T(5n/7 + ...) + O(n)\) → 仍线性(系数 \(1/7 + 5/7 < 1\))。

        • 分组 3:每组中位数 ≥/≤ 中位数之中位数的元素数仅 2 个(\(2n/6 - 2\) 个),递归式 \(T(n/3) + T(2n/3 + ...) + O(n)\) → 不能保证线性(系数之和 ≥ 1)。

      When

      1. 「最坏线性」理论需要 → SELECT。

      2. 「实现简单 + 常数小」 → RANDOMIZED-SELECT(实践中更常用)。

      3. SELECT 常数因子大,主要用于理论应用(如它们对外部选择也有用)。

      Example:n = 15 时的 SELECT 分组(图 9.1):3 组各 5 个;每组中位数 = 第 3 小;中位数之中位数 = 这 3 个中位数的中位数 = 第 2 小的中位数;用此 pivot 划分,至少 6 个元素被丢弃到任一边。

      9.4 两种选择算法的对比与应用

      What:把 RANDOMIZED-SELECT 与 SELECT 与直接排序比较——得出实践选择准则。

      Why:选择实现时常常面临「RANDOMIZED-SELECT vs SELECT vs 先排序」的选择——理解三者差异可以做出明智决策。

      How:对比矩阵:

      算法 最坏时间 期望时间 实践考虑

      先排序 + 取第 i 位

      \(\Theta(n \lg n)\)

      \(\Theta(n \lg n)\)

      实现最简单;i 多时更值

      RANDOMIZED-SELECT

      \(\Theta(n^2)\)

      \(\Theta(n)\)

      实践中常数小;攻击者可能构造最坏

      SELECT

      \(\Theta(n)\)

      \(\Theta(n)\)

      常数大;用于实时 / 安全场景

      堆(先建堆 + 删 i 次)

      \(\Theta(n)\) + i \(\Theta(\lg n)\)

      适合「i 小」的「top-i」问题

      应用场景:

      1. 「i 已知且不变化」 → SELECT 一次性定位 + PARTITION 得第 i 小附近所有元素。

      2. 「i 是 top-k」 → 堆或 quickselect 变体。

      3. 「多次不同 i」 → 排序(指数次查询摊销后仍便宜)。

      4. 「中位数(i = n/2)」 → SELECT 或 RANDOMIZED-SELECT;中位数在分治算法中常用于「保证平衡」(如 quicksort median-of-three)。

      When:实际工程中 RANDOMIZED-SELECT 是默认选择;SELECT 用于教学 / 最坏有界场景。

      Example

      • 找数组的中位数 → RANDOMIZED-SELECT(k = ⌊n/2⌋)。

      • 找 k 个最接近中位数的元素(练习 9.3-7) → 先用 SELECT 找中位数,再用线性时间取 |A[i] - med| 最小的 k 个。

      • Top-10 from 1M docs → 建最大堆(O(n))+ 10 次 EXTRACT-MAX(O(10 lg n))——比排序快。

      三、关键图表

      本章无独立编号图表

      本章核心图(图 9.1,SELECT 划分分析)以「5 个一组按列摆放、白色为中位数、箭头从大指向小」的视觉形式呈现——本笔记用文字 + 公式表述;原书图见 CLRS 第 4 版第 222 页。

      核心公式对照表
      式编号 内容

      最小/最大下界

      至少 \(n - 1\) 次比较

      同时最小 + 最大比较数

      \(\lfloor 3n/2 \rfloor\)(成对法);下界 = \(\lfloor 3n/2 \rfloor - 2\)(练习 9.1-2)

      RANDOMIZED-SELECT 最坏

      \(\Theta(n^2)\)(连续选最差 pivot)

      RANDOMIZED-SELECT 期望

      \(\Theta(n)\)

      式 9.1

      指示变量 \(E[X_k\) = 1/n]

      RANDOMIZED-SELECT 递归式

      \(E[T(n)\) \leq \frac{2}{n} \sum_{k=\lfloor n/2 \rfloor}^{n-1} E[T(k)] + O(n)]

      SELECT 中位数侧元素数下界

      至少 \(3n/10 - 6\) 大于 x,至少 \(3n/10 - 6\) 小于 x

      式 9.2

      SELECT 代入法要求:\(-cn/10 + 7c + an \leq 0\),即 \(c \geq 20a\)、\(n \geq 140\)

      SELECT 递归式

      \(T(n) = T(\lceil n/5 \rceil) + T(7n/10 + 6) + O(n)\)

      四、思维导图

      mindmap
        root((第 9 章 中位数与顺序统计量))
          基本概念
            第i小
            中位数
            下上中位
          最小最大
            n-1比较
            成对法
            3n/2
          RANDOMIZED
            仿快速排序
            单边递归
            期望Θ(n)
            最坏Θ(n²)
          SELECT
            5个一组
            中位之中位
            至少砍30%
            最坏Θ(n)
          对比
            实现简单
            常数因子
            实时vs平均
            排序+索引
          应用
            中位数分治
            top-k
            区间元素

      五、重点与易错点

      1. 「中位数 vs 平均数」的概念差异:中位数是「顺序统计量」,平均数是「数值平均」;本章明确处理中位数。

      2. 下/上中位数只对偶数 \(n\) 有差:本书默认「下中位数」= 第 \(\lfloor (n+1)/2 \rfloor\) 小;实现时可参数化。

      3. MINIMUM 的比较下界 \(n-1\) 是紧的:MINIMUM 用 \(n-1\) 次比较,达下界——最优。

      4. 同时最小 + 最大不是 \(2(n-1)\) 而是 \(\lfloor 3n/2 \rfloor\):常见错误是分开两次 MINIMUM/MAXIMUM。

      5. 成对比较的初始化要小心:\(n\) 偶时前两个元素先比 1 次定 min/max;\(n\) 奇时把第一个同时作为初始 min 和 max。

      6. RANDOMIZED-SELECT 不能排序:它只找一个元素;但可结合 PARTITION 排序(问题 9-1c)——用 SELECT + PARTITION 排序期望 \(O(n \lg n)\)。

      7. RANDOMIZED-SELECT 期望 \(\Theta(n)\) 但最坏 \(\Theta(n^2)\):连续选最差 pivot 是罕见但理论可能——McIlroy 1999 证明可针对具体非随机化实现构造 O(n²) 输入(与第 7 章同模式)。

      8. SELECT 的 5 不是魔法数——7 也行(练习 9.3-1)但常数更大;3 不行(递归式不收敛)。一般地,分组大小 = 奇数 \(g\) 时,递归式要求 \(1/g + (g-2)/(g+1) < 1\),等号解为 \(g = 5\)。

      9. SELECT 实际不常用:常数因子大——实践中 RANDOMIZED-SELECT 更受青睐,SELECT 主要是教学价值。

      10. 「只递归一边」是期望线性的关键:和 quicksort 不同,quicksort 递归两边 → \(\Theta(n \lg n)\);只递归一边 → 期望线性。

      11. 代入法中「假设 \(T(n)\) 是单调递增」:选取 max(k-1, n-k) 而不是具体哪一边是上界分析技巧——便于求和且确保上界。

      12. 「145 元素时调用 SELECT 得中位数」:常数很大的现实意味着 SELECT 在小输入下用插入排序更划算(与 quicksort 优化同理)。

      13. 黑盒中位数 = 线性选择(练习 9.3-5):若已知 O(n) 的中位数算法,可用 PARTITION 替换 RANDOMIZED-PARTITION,对外提供「线性选择」接口。

      14. 问题 9-1c(排序 + 索引 + 排序):用 SELECT 找第 i 大 + PARTITION + 排序 i 个最大的 = \(\Theta(n + i \lg i)\),比纯排序 \(O(n \lg n)\) 省。

      15. 「线性选择不与排序下界矛盾」:因为选择问题比排序弱——选择不需要输出序列的排列。本章是「上界匹配下界」的精彩演示。

      补充:与其他章节的衔接

      1. 第 2 章:MINIMUM/MAXIMUM 体现「单循环 + 维护当前最值」的模式;与插入排序的内层循环同源。

      2. 第 7 章:RANDOMIZED-SELECT 直接借用 RANDOMIZED-PARTITION;两者的差异(双边 vs 单边递归)是关键设计决策。

      3. 第 8 章:本章是「不排序也能达到线性」的存在性证明,与第 8 章的「比较排序必须 \(\Omega(n \lg n)\)」形成对比。

      4. 第 13 章 (红黑树):第 13 章的中位数化方法是「顺序统计树」(OS-tree),由 §14.1 给出——本章 SELECT 是其前身思想。

      5. 第 23 章 (最小生成树):Kruskal 与 Prim 都用「最小生成树边的中位数 / 优先级」优化;选择问题在此作为子程序。

      6. 第 27 章 (并行算法):并行 SELECT 选择 pivot 时多线程加速;第 9 章只是开始。