算法导论第 9 章:中位数与顺序统计量
主要内容
本章讨论顺序统计量,即某个集合中大小排名第几的元素。
比如最小值是第一个顺序统计量,最大值是最后一个顺序统计量,中位数是集合的中点元素。如果元素有奇数个,有唯一的中位数;如果元素有偶数个,有两个中位数,一般选取较小的那个。将问题一般化,则是从含有互异元素的集合中找到特定大小排名的元素。
最小值和最大值
在一个含有\(n\)个元素的集合中,最多需要\(n-1\)次比较就能确定最小元素。下面是代码实现:
1 | int mininum(int A[], int n) |
这是是最好的结果,对于最小值问题,至少也需要\(n-1\)次比较。
在某些应用中,我们需要同时找到最大值和最小值。最简单的办法是分别独立地找到最大值和最小值,那么一共需要\(2n-2\)次比较。实际上有更好的办法,可以对输入元素成对地处理:首先将它们相互比较,然后把较小的与当前最小值比较,把最大的与当前最大值比较。这样每两个元素共需要3次比较。总的比较次数至多是\(3 \lfloor n/2 \rfloor\)。
期望为线性时间的选择算法
一般选择问题看起来要比找最小值这样的简单问题更难,但其实这两个问题的渐进运行时间同为\(\Theta(n)\)。本节介绍一种解决选择问题的分治算法RANDOMIZED-SELECT
。类似于快速排序,对数组进行递归划分。但不同的是,快速排序递归处理划分的两边,而该算法只处理划分的一边,所以性能也有差异。实现要用到第七章介绍的快速排序中的randomized_partition
函数,代码如下:
1 | int randomized_select(int A[], int p, int r, int i) |
可以证明,上述算法的期望运行时间为\(O(n)\),即,若所有元素是互异的,在期望线性时间内,可以找到任一顺序统计量,特别是中位数。
最坏情况为线性时间的选择算法
还有一个最坏情况下运行时间为\(O(n)\)的选择算法SELECT
,也通过对数组的递归划分来找出所需元素。过程和代码从略。