算法导论 总结索引 | 第二部分 第九章:中位数和顺序统计量

1、为了简便起见,所用的“中位数”都是指下中位数

2、讨论 从一个由n个互异的元素构成的集合中 选择第i个顺序统计量的问题。为了方便起见,假设集合中的元素 都是互异的,但实际上我们所做的 都可以推广到集合中包含重复元素的情形

将这一问题形式化定义为 如下的选择问题
输入:一个包含n个(互异的)数的集合A和一个整数i,1≤i<=n
输出:元素x∈A,且A中怡好有i-1个其他元素小于它

3、可以在 O(n lgn) 时间内解决这个问题,可以用 堆排序或归并排序 对输入数据进行排序,然后在 输出数组中 根据下标找出第i个元素即可。本章 将介绍更快的算法

在9.1节中,我们将讨论从一个集合中选择最小元素和最大元素的问题;9.2节将分析一个实用的随机算法,它在元素互异的假设条件下 可以达到 O(n) 的期望运行时间;9.3节给出 更具理论意义的算法,在最坏情况下的运行时间为O(n)

1、最小值和最大值

1、需要做多少次 比较 才能确定其最小元素呢?我们可以很容易地 给出n-1次比较这个上界:依次遍历 集合中的每个元素,并记录下当前最小元素

假设 该集合元素存放在数组A中,且 A.length = n

MINIMUM(A)
	min = A[1]
	for i = 2 to A.length
		if min > A[i]
			min = A[i]
	return min

对于确定最小值问题,我们可以得到其下界就是n - 1次比较;对于任意一个确定最小值的算法,可以把它看成是在各元素之间进行的一场锦标赛。除了最终获胜者以外,每个元素 都至少要输掉一场比赛。因此,我们得到结论:为了确定最小值,必须要做n - 1次比较,因此,从所执行的比较次数来看,算法MINIMUM是最优的

2、只需要 最多 3⌊n/2⌋ 次比较 就可以同时找到最小值和最大值。具体的方法是 记录已知的最小值 和 最大值
但是 不是将每一个输入元素 与 当前最小值和最大值进行比较(这样代价 每个元素都需要比2次)

而是 对输入元素成对地 进行处理。首先,我们将 一对输入元素 相互进行比较,然后把较小的 与 当前最小值比较,把较大的与当前最大值进行比较,这样 每两个元素 共需3次比较

初始值的选取 依赖于n是奇数还是偶数。如果n是奇数,将 最小值和最大值的初值 都设为第一个元素的值,然后成对地 处理余下的元素;如果n是偶数,就对 前两个元素做一次比较,以 决定最小值和最大值的初值,然后 成对地处理余下的元素

如果n是奇数,总共进行 3⌊n/2⌋次比较。如果n是偶数,则 先进行一次初始比较,然后 进行3(n-2)/2 次比较,共 3n/2 - 2
所以总的比较次数最多 3⌊n/2⌋

3、证明,在最坏情况下,找到n个元素中第二小的元素 需要n+⌈lgn⌉-2次比较
n+⌈lgn⌉-2 =
n+⌈lgn⌉-2 =

在寻找最小元素的过程中,先将输入元素两两分组进行比较,再将所有组中较小的元素两两分组进行比较,重复此步骤直至找到最小元素,易得:与最小元素比较过的元素数为⌈lgn⌉,从这些元素中找到第二小的元素需要⌈lgn⌉−1次比较。因此,在最坏情况下,找到n个元素中第二小的元素需要n+⌈lgn⌉−2 次比较

2、期望为线性时间的选择算法

1、选择问题 跟 找最小值 的渐进运行时间都是 Θ(n)。本节介绍 一种解决选择问题的 分治算法(RANDOMIZED-SELECT)

与快速排序 不同的是,快速排序 会递归处理 划分的两边,而 RANDOMIZED-SELECT 只处理划分的一边,不需要 全部排序。所以 快速排序的 期望运行时间是 Θ(n lgn) ,而 RANDOMIZED-SELECT 的期望运行时间为 Θ(n)

与 RANDOMIZED-QUICKSORT(随机快排) 一样,它的部分行为 是由随机数生成器的输出 决定的,所以 RANDOMIZED-SELECT 也是一个随机算法
算法返回 数组A[p … r] 中第i小的元素

RANDOMIZED-SELECT(A, p, r, i)
	if p == r
		return A[p]
	q = RANDOMIZED-PARTITION(A, p, r)
	k = q - p + 1
	if i == k // 主元就是答案
		return A[q]
	else if i < k
		return RANDOMIZED-SELECT(A, p, q - 1, i) // 只要处理一边
	else return RANDOMIZED-SELECT(A, q + 1, r, i - k);

使得A[p…q - 1] 中的每个元素 都小于或等于 A[q],而A[q] 小于 A[q+1…r] 中的每个元素。与快速排序中一样,我们称A[q] 为主元
检查A[q] 是否是 第i小的元素。如果是,就返回 A[q],否则,算法要确定 第i小的元素 落在两个子数组 A[p… q-1] 和 A[q+1…r] 的哪一个之中。如果i<k,则要找的元素 落在划分的低区

2、RANDOMIZED-SELECT 的最坏情况运行时间为 Θ(n2),找最小元素 也是如此,因为在 每次划分时 可能总是按余下的元素中 最大的来进行划分,而划分的操作 需要 Θ(n) 时间

该算法有线性的 期望运行时间,又因为是随机化的,所以 不存在一个特定的 会导致其最坏情况发生的 输入数据

该算法在数组A[p…r] 上的运行时间是一个随机变量,记为 T(n),可以得到 E[T(n)]的一个上界:程序 RANDOMIZED-PARTITION 能等概率地返回 任何元素作为主元
定义指示器变量
假设元素互异
为了得到上界,假定第i个元素 总是在划分中 包含比较大元素的一边
对于指示器随机变量 Xk 恰好在给定的k值上 取值1,对其他值都为0
递归式
两边取期望值
公式
公式
因为 Xk 和 T(max(k - 1, n - k)) 是独立的随机变量

对于 max(k - 1, n - k),有
公式
如果n是偶数,则从 T(⌈n/2⌉) 到 T(n - 1) 的每一项 在总和中恰好出现两次。如果n是奇数,除了T(⌊n / 2⌋) 出现一次外,其他这些项 也都会出现两次
公式
用替代法来得到 E[T(n)] = O(n)。对满足这个递归式初始条件的 某个常数c,有 E[T(n)] <= cn。上式中 O(n) 项所描述的函数 有上界 an
公式
对足够大的n,最后一个表达式 至多是 cn,等价地,选取满足 cn/4 - c/2 - an >= 0 即可

3、RANDOMIZED-SELECT的一个基于循环的版本

RANDOMIZED-SELECT-ITERATIVE(A, p, r, i)
    while r ≥ p
        if p == r
            return A[p]
        q = RANDOMIZED-PARTITION(A, p, r)
        k = q - p + 1
        if i == k        // 主元是答案
            return A[q]
        else if i < k
            r = q - 1
        else
            p = q + 1
            i = i - k

3、最坏情况为线性时间的选择算法

1、最坏情况运行时间为 O(n) 的选择算法。像RANDOMIZED-SELECT一样,SELECT算法 通过对输人数组的递归划分 来找出所需元素,但是 在该算法中能够保证得到 对数组的一个好的划分。SELECT使用的 也是来自快速排序的确定性划分算法 PARTITION,但做了修改,把划分的主元 也作为 输入参数

2、算法 SELECT 可以确定一个有 n>1 个不同元素的输入数组中 第i小的元素(如果n=1,则 SELECT 只返回它的唯一 输入数值作为 第i小的元素)
1)将输人数组的n元素划分为 ⌊n/5⌋ 组,每组5个元素,且至多只有一组由剩下的 n mod 5 个元素组成
2)寻找这⌈n/5⌉组中每一组的中位数:首先对每组元素进行插人排序,然后确定每组有序元素的中位数
3)对于 第二步找出的 ⌈n/5⌉ 个中位数,递归调用 SELECT 以找出其 中位数x(如果有偶数个 中位数,为了方便,约定x是较小的中位数)
4)利用 修改后的 PARTITION 版本,按中位数的中位数x 对输入数组 进行划分。让k比 划分的低区中的元素数目 多1,因此x是第k小的元素,并且有n - k个元素在划分的高区
5)如果 i=k,则返回x。如果i<k,则在低区递归调用 SELECT 来找出 第 i 小的元素。如果i > k,则在高区 递归查找 第i-k小的元素

3、至少 有一半大于或等于中位数的 中位数x。在 ⌈n / 5⌉个组中,除了 当n不能被5整除时 产生的所含元素 小于5的那个组 和 包含x的那个组之外,至少有一半的组中 有3个元素大于x
公式
类似的,至少有 3n/10-6 个元素小于x。因此在(5)步中,SELECT的递归调用 最多作用于 7n/10+6(n - (3n/10 - 6)) 个元素
对算法 SELECT 分析
解释
步骤1,2,4 需要 O(n) 时间。步骤3 所需时间为 T(⌈n/5⌉),步骤5 所需的时间至多为 T(7n/10 + 6)
可以得到 如下递归式:
递归式
假设对 n<140 (P 139) 有 T(n) <= cn
式子
因为假设 n>140,所以有 n/(n - 70) <= 2,因此,选择 c >= 20a 就能够满足不等式。因此 最坏情况下 SELECT 的运行时间是线性的

4、本章中的 线性时间选择算法 不需要任何关于输入的假设。它们不受限于Ω(n lgn) 的下界约束,因为它们 没有使用排序就解决了选择问题。因此,在本章引言部分介绍的排序 和 索引方法 不是解决选择问题的 渐近高效率方法

5、在算法SELECT中,如果输入元素被分为每组7个元素,该算法仍然会是线性时间
证明:在算法SELECT中,如果输入元素被分为每组3个元素,SELECT的运行时间不是线性的

在这⌈n/3⌉个组中,除了当n不能被3整除时产生的所含元素少于3的那个组和包含x的那个组之外,至少有一半的组中有2个元素大于x。不算这两个组,大于x的元素个数至少为:2(⌈1/2⌈n/5⌉⌉−2)⩾n/5−4。类似地,至少有n/5-4个元素小于x。因此,在最坏情况下,在第5步中,SELECT的递归调用最多作用于 4n/5+4 个元素
递归式:T(n)⩽T(⌈n/3⌉)+T(4n/5+4)+O(n)。用替换法来证明这个运行时间不是线性的。更明确地说,将证明对某个适当大的常数c和所有的n>0,有T(n)!<=cn
首先,挑选一个常数a,使得对所有的n>0,上述公式中的O(n)项所对应的函数(用来描述算法运行时间中的非递归部分)有上界an。其次,假设对某个适当大的常数c,有T(n)⩽cn。将这个归纳假设代入上述递归式的右边,得到:T(n)⩽c⌈n/3⌉ + c(4n/5 + 4) + an ⩽ cn/3 + c + 4cn/5 + 4c + an = 17cn / 15 + 5c + an = cn + (2cn / 15 + 5c + an) > cn
所以,T(n)!<=cn

6、分析SELECT,如果n≥140,则至少⌈n / 4⌉个元素大于中位数的中位数,至少⌈n / 4⌉ 个元素小于x(9.3-2)

可以像SELECT算法那样,在PARTITION中按中位数的中位数x 对输入数组进行划分。在最坏情况下,每次划分都将数组分成1/4和3/4两个部分,可以得到如下递归式:T(n)=T(1/4n)+T(3/4n)+O(n),T(n)=O(nlgn)

7、假设已经有了一个最坏情况下是线性时间的用于求解中位数的“黑箱”子程序。设计一个能在线性时间内解决任意顺序统计量的选择问题算法

LINEAR-SELECT(A, p, r, i)
    if p == r
        return A[p]
    o = MEDIAN-SELECT(A, p, r)
    exchange A[r] with A[o]
    q = PARTITION(A, p, r)
    k = q - p + 1
    if i == k
        return A[q]
    else if i < k
        return LINEAR-SELECT(A, p, q-1, i)
    else return LINEAR-SELECT(A, q+1, r, i-k)

8、对于一个包含n个元素的集合来说,k分为数就是指能把有序集合分成k个等大小集合的“k-1个顺序统计量”,给出一个能找出某一集合的k分位数的O(n logk)的算法
首先k要整除n,这样才可以分为k个等大小的集合。若将一个大小为n的集合按照顺序排好,我们所要求的这k-1个数就是要把这个集合平均分为k个集合。例如集合A= { 8, 4,0, -89, -12, 0, 36, 789, 21},将集合排序后A={-89,-12,0,0,4,8,21,36,789};若k=3,则将集合分为{-89,-12,0},{0,4,8},{21,36,789}那么2个顺序统计量为0,8

设d=n/k,于是这k-1个数是集合A中第d,2d…(k-1)d小的数。我们知道利用Select算法(选择的O(n)算法)找出第i小的数算法复杂度为O(n),那么逐一的找出这k-1个数时间为O(nk),而题目给出的时间是O(nlogk).于是需要结合二分的思想。 首先将第(k/2)*d小的数找出来放在A中正确的位置(数组A[0…n-1],则它正确的位置下标为(k/2)*d-1)),然后数组A[o…n-1]分为两部分A[0…(k/2)*d-2]和A[(k/2)*d…n-1],递归的放置其他的k-2个数。这样最终时间复杂度为O(n logk)

来自:https://blog.csdn.net/Tander_Tang/article/details/50760724

void Select_K(int *a, int p, int r, int k){
	if (k == 0)
		return;
	int d= (r - p)/k;
	Select(a, p, r, (k / 2)*d);  
	Select_K(a, p, (k / 2)*d-1, k / 2); 
	Select_K(a, (k / 2)*d, r, k / 2);
}

9、设X[1…n]和Y[1…n]为两个数组,每个都包含n个有序的元素。设计一个O(lgn) 时间的算法来找出数组X和Y中所有 2n个元素的中位数

MEDIAN-IN-TWO-ARRAYS(X, a, b, Y, c, d)
    if X[(a+b) / 2] == Y[(c+d) / 2]
        return X[(a+b) / 2]
    else if X[(a+b) / 2] < Y[(c+d) / 2]
        MEDIAN-IN-TWO-ARRAYS(X,(a+b)/2, b, Y, c,(c+d)/2- 1)
    else
        MEDIAN-IN-TWO-ARRAYS(X, a,(a+b)/2- 1, Y,(c+d)/2, d)

X[⌊(a+b) / 2⌋] < Y[⌊(c+d) / 2⌋] 说明在区间 [⌊(a+b) / 2⌋, b] 与 [c, ⌊(c+d) / 2⌋ - 1] 中

  • 15
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值