《算法导论3rd第九章》中位数和顺序统计学

前言

本章如果要归结成一个问题的话,可以归结为选择问题,比如要从一堆数中选择最大的数,或最小的数,或第几小/大的数等。

中位数和顺序统计量

选择问题:给定一个包含n个元素的集合A和一个整数i,1<=i<=n,我们需要得到一个整数x,其中有i-1个元素小于它,即第i个顺序统计量。

  • 顺序统计量:在一个n个元素组成的集合中,第i个顺序统计量是该集合中第i小的元素。
  • 中位数:它所属集合的“中点元素”。如果集合元素n为奇数,则中位数为(n+1)/2处;如果n为偶数,则中位数出现在n/2(下中位数)和n/2+1(上中位数)处,一般无特殊说明,我们都取下中位数。
  • 最大值:第1个顺序统计量。
  • 最小值:第n个顺序统计量。

这个问题最直观的解法是通过排序+索引的方式,但排序算法有多种,且时间复杂度略高。我们需要更低时间复杂度来解决这个问题,要求线性时间,即O(n)。

最大值、最小值

同时求最大最小值,采用最直观朴素的解法就能解决。就是一个个比较,时间复杂度O(n),已经没有比这更优的了。代码如下:

/***********线性时间求最小值************/
int Minimun(int arr[], int n)
{
    
    
    int nMin = arr[0];
    for(int i = 1; i < n; i++)
        //min
        if(nMin > arr[i])
            nMin = arr[i];
        //max
//         if (nMax < arr[i])
//             nMax = arr[i];
    return nMin;
}

按照上述算法,同时求最大最小值,需要2(n-1)次比较,但是换一种思路,我们没必要一个元素比较两次,而是两个元素比较一次,然后得出大小关系,在分别和最大、最小值比较,这样两个元素就只用比较3次,总共就是3/2n次。这里要分奇偶数看待,但不管奇偶,都需要3/2n次。比较次数减少了,时间也就降低了。

练习

在这里插入图片描述

9-1

首先,我们先将数组中的元素两两成对比较,共需n/2次比较,那么就有n/2个元素是较小的元素,然后再将这些较小的元素再次两两成对比较,又淘汰一半,重复这样的循环,每次淘汰一半元素直到只剩下1个元素,该元素就是最小元素。在这里插入图片描述

  1. 如上图所示,经过的比较次数为S=n/2+n/4+…(n/(2^k)=1) k=lgn S=n-1次。
  2. 第2小的元素肯定与最小元素比较过,所以我们采取的方法是,从根结点(最小元素)开始沿着根向叶子结点开始查找等于根节点的子结点A,第二小的元素就应该在与A结点属于同一个父结点的另外一个子结点B上,将B结点上的值给予第二小元素second,这样经过以上方式最坏lgn-1次比较

1-2

即分析3/2n次比较算法。分别从n为奇偶前提出发。

  1. 先拿出2元素比较,表示最大,最小,即1次
  2. 如果是偶数, 剩下的 3(n-2)/2次比较
  3. 如果为奇数,剩下的 3(n-3)/2次比较,外加2次分别和最大最小比较
    在这里插入图片描述

以期望线性时间做选择

一般选择问题:找出数组中第i大的元素。看起来比找最小值要复杂,但实际上两种问题的渐进运行时间相同,都是Θ(n)。Randomized-Select的这个算法很强悍,期望的时间复杂度就能达到O(n),但最坏情况下的时间复杂度却为O(n^2)。该算法采用的是快速排序章节中的Partition过程来得到划分的中点,如果该中点恰好等于选择的点,则即为所求,否则再在左右两个区间中用同样的方法再次寻找,伪代码如下:

RANDOMIZED-SELECT(A, p, r, i)
1  if p = r
2      then return A[p]
3  q ← RANDOMIZED-PARTITION(A, p, r)
4  k ← q - p + 1
5  if i = k          ▹ the pivot value is the answer
6      then return A[q]
7  elseif i < k
8      then return RANDOMIZED-SELECT(A, p, q - 1, i)
9  else return RANDOMIZED-SELECT(A, q + 1, r, i - k)

假设程序RANDOMIZED-SELECT以相等的可能性返回任何元素作为主元,即复杂度为S=n/2+n/4+…n/(2^k),即O(n)。。具体的复杂度求法,参考书本。

练习

在这里插入图片描述

2-1

(略)

2-2

(略)

2-3

RANDOMIZED-SELECT(A, p, r, i)
    while true
        if p == r
            return A[p]
        q = RANDOMIZED-PARTITION(A, p, r)
        k = q - p + 1
        if i == k
            return A[q]
        if i < k
            r = q
        else
            p = q
            i = i - k
 
 RANDOMIZED-PARTITION(A, p, r)
    x = RANDOM(p - 1, r)
    swap A[x] with A[r]
    return PARTITION(A, p, r)

PARTITION(A, p, r)
    x = A[r]
    i = p
    for k = p - 1 to r
       if A[k] < x
           i = i + 1
           swap A[i] with A[k]
    i = i + 1
    swap A[i] with A[r]
    return i           

2-4

每次都选择最大元素作为划分主元,这样就有T(n)=T(0)+T(n-1)+O(n)产生最坏运行时间。

最坏限行时间的选择

Randomized_Select在最坏情况下,时间复杂度为O(n^2).现在来看下最坏情况下复杂度为O(n),Select算法。基本思想是保证对数组的划分是个好的划分,所以,需要对Partition做一点修改,具体的算法步骤如下

  1. 将输入数组的n个元素划分为n/5(上取整)组,每组5个元素,且至多只有一个组有剩下的n%5个元素组成。
  2. 寻找每个组织中中位数。首先对每组中的元素(至多为5个)进行插入排序,然后从排序后的序列中选择出中位数。
  3. 对第2步中找出的n/5(上取整)个中位数,递归调用SELECT以找出其中位数x。(如果是偶数去下中位数)
  4. 调用PARTITION过程,按照中位数x对输入数组进行划分。确定中位数x的位置k。
  5. 如果i=k,则返回x。否则,如果i<k,则在地区间递归调用SELECT以找出第i小的元素,若干i>k,则在高区找第(i-k)个最小元素。
    在这里插入图片描述

如上图,即大于x的元素个数至少有:
3 ( ⌈ 1 2 ⌈ n 5 ⌉ ⌉ − 2 ) ≥ 3 n 10 − 6 3(\lceil {1 \over 2}\lceil{n \over 5}\rceil\rceil -2) \ge {3n \over 10} -6 3(215n2)103n6
同样,小于x的元素也至少有3n/10 - 6;因此在第5步,最坏情况有 7 n 10 + 6 {7n \over 10} + 6 107n+6 个元素递归调用Select.进而可以得到其递归式为:
在这里插入图片描述
运用代入法,我们可以得到其时间复杂度为O(n).

练习

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

在这里插入图片描述

3-2分析SELECT,并证明:如果n≥140,则至少ceil(n/4)个元素大于中位数的中位数x,至少ceil(n/4)个元素小于x?

在这里插入图片描述

3-3 假设所有元素都是互异的,说明在最坏情况下,如何才能使快速排序的运行时间为Ο(nlgn)?

快速排序对主元的划分决定了其运行时间,如果最坏是Ο(nlgn),那么就不允许出现极端划分情况。我们可以使用Select算法的思路,选中位数作为主元的方法来避免极端情况的发生。

3-4 假设对一个含有n个元素的集合,某算法只用比较来确定第i小的元素。证明:无需另外的比较操作,它也能找到比i小的i-1个元素和比i大的n-i个元素。

因为在SELECT函数查找第i个元素时,那么就会以这个元素作为主元对整个数组进行划分,低区的i-1个元素肯定都是小于主元的,高区n-i个元素肯定都是大于主元的。

3-5 假设已经有了一个用于求解中位数的“黑箱”子程序,它在最坏情况下需要线性运行时间。写出一个能解决任意顺序统计量的选择问题的线性时间算法。

使用“黑箱”子程序找到中间值。然后利用中间值根据选择问题,依次递归查找,即可。

3-6 对一个含有n个元素的集合来说,所谓k分位数(the kth quantile),就是能把集合分成k个大小相等的集合的k-1个顺序统计量。给出一个能输出某一集合的这k-1个顺序统计量的O(nlgk)时间的算法。

理解题目的意思,找到k-1个集合,这里并没有要求集合是里面是排序的,用Select的方式,将大集合一直二分。

3-7 给出一个O(n)时间的算法,在给定一个有n个不同数字的集合S以及一个正整数k≤n后,它能确定出S中最接近其中位数的k个数

如果给出在线性时间内的算法,那么可能要用到最坏为线性时间的查找第i小元素的子程序SELECT。我们先找到这n个数的中位数,然后以此中位数为中心,左边距离中位数k/2个远的位置是这k个数的左端点,右边距离中位数k/2个远的位置是这k个数的右端点。用SELECT函数找到这三个数(中位数,左边距离中位数k/2的数,右边距离中位数k/2的数)就可以确定最接近其中位数的k个数

3-8 设 x[1…n]和Y[1…n]为两个数组,每个都包含n个已排序的数。给出一个求数组X和Y中所有2n个元素的中位数的O(lgn)时间的算法。

主要思路参考《漫画:如何找到两个数组的中位数?

MEDIAN(X, Y, n)
    if n == 1
        return min(X[1], Y[1])
    if X[n / 2] < Y[n / 2]  
        return MEDIAN(X[n / 2 + 1..n], Y[1..n / 2], n / 2)
    return MEDIAN(X[1..n / 2], Y[n / 2 + 1..n], n / 2)

3-9

(略)

主要参考

算法导论第九章中位数和顺序统计量(选择问题)
算法导论第九章课后答案
Medians and Order Statistics

猜你喜欢

转载自blog.csdn.net/y3over/article/details/121465013