中位数和顺序统计量(第9章)

学习由n个不同的树的集合中选择第i个顺序统计量的问题。
输入:一个包含n个(互异的)数的集合A,1in的整数i。
输出:元素xA,并且 A中恰好有i个元素小于它。

求解第i小的元素的算法设计

1. Max 和 Min

在一个有n个元素的集合中,确定Max 和 Min的方法是遍历。

同时找到最大值和最小值,我们只需要最多3n/2次比较就可以确定。具体的方法是对输入的元素成对的处理,首先我们将一对输入元素进行比较,然后将较小的与最小值比较,较大的与最大值比较。这样每两个元素仅需要三次比较。

2. 期望时间是线性时间的选择算法

//划分算法 A[q]作为划分数组的主元
PARTITION(A,p,q){
    x=A[q]
    i=p-1
    for j=p to q-1
        if(A[j]<=x){//将比A[q]小的数值统一按照顺序移动到前面
            i=i+1
            exchange A[i] with A[j]
        }
    exchange A[i+1] with A[q]
    return i+1; 
}

//随机划分算法 随机选择主元
RANDOMIZED-PARTITION(A,p,q){
    i=Random(p,q)
    exchange A[q] with A[i];
    return PARTITION(A,p,q)
}


//Algorithms 
//这里不是从整个数组中找,而是从其中的一段中找  递归的算法设计如下
RANDOMIZED-SELECT(A,p,q,i){
    if(p==q)//检查递归的基本情况,A中仅仅包括一个元素这种情况下 i=1.
        return A[p]
    r=RANDOMIZED-PARTITION(A,p,q)//随机划分函数
    k=r-p+1;//小于A[r]的元素的个数的计算 
    if(i==k){
        return A[r]
    else if(i<k)
        RANDOMIZED-SELECT(A,p,r-1,i)
    else
        RANDOMIZED-SELECT(A,r+1,q,i)
    }

}

如上 RANDOMIZEDSELECT(A,p,q,i)所示,在算法中首先检查数组仅包含一个元素的情况,i=1.其他的情况,我们调用随机划分函数RANDOMIZEDPARTITION(A,p,q),类似于快速排序中的思路,将数组A划分为A[p....r1]A[r+1...q],A[r].然后计算前面部分的元素个数k=rp+1;//A[r] ,后面进行检查和递归求解。

3. 最坏情况为线性的选择算法

  指示器随机变量(indicator random variable):给定一个样本空间S和 事件A,那么事件A对应的指示器随机变量I{A}=1(如果A发生),0(如果A不发生);显而易见,事件A对应的指示器随机变量的期望等于事件A发生的概率。实例分析见指示器随机变量

  类似于RANDOMIZED-SELECT算法,SELECT通过对输入数组的递归划分来找出所需元素,但是该算法能够保证对数组的一个好的划分。核心就是在划分主元的确定上,将PARTITION中的主元也作为输入参数进行输入。
  具体算法的文字描述:

Step 1:把数组划分为若干个子数组,每个子数组里包含5个数,因为会有无法整除的可能,所以最后一个子数组可能会小于5.
Step 2:用插入排序把每个组的5个数排序,然后找出中位数。
Step 3:把获得的中位数又排序,找出中位数的中位数x。如果中位数的个数是偶数,那么取排好序的第 m/2 个数,m指的是中位数的个数。
Step 4:把原来的数组使用类似快排的方法,分成两个部分。一部分比x大,一部分比x小。我们可以假设左边的数小,右边的数大。然后我们可以得到“中位数的中位数”的位置i.
//这个地方是调用修改后的PARTITION,返回当前x的位置。 假设下标从1开始。
Step 5:如果i = k, 那么那个“中位数的中位数”就是第小的数。如果 i < k, 则在低区递归调用SELECT来找出第i小的元素,如果i > k, 则在高区递归查找第i-k大的元素。

分析算法:
  整个过程中,第1,2,4步所需时间为O(n), 注意第2步的复杂度不为O(n^2),第3步的复杂度为 T(n/5),第五步的复杂度为 T(7n/10)。注意这里第2步虽然我们使用的是插入排序,但是待排的序列长度为常数5,所以对一组的排序时间花费为O(1),对于n/5个组,其时间预期是O(n/5),即O(n)。
  时间预期为T(n)<=T(n/5)+T(7n/10+6)+O(n)

算法伪代码描述:

//设定主元参数的划分函数
PARTITION(A,p,q,key){
    i=p-1;

    for j=p to q-1{
        if(A[j]<=key){//将比A[q]小的数值统一按照顺序移动到前面
            i=i+1
            exchange A[i] with A[j]
        }   
    }
    return i+1; 
}
//---------------------------------
SELECT(A,p,q,i){
    if(A.length<5){
        InsertSort(A,p,q);
        return A[i];
    }

    groups=A.length/5;
    midValues[groups]=0;//声明初始化一个中位数数组
    for(int t=0;t<groups;t++){
        InsertSort(A,t*5,t*5+5);
        midvalues[t] = A[t*5+3];//中
    }
    InsertSort(midvalues,0,groups);
    if(groups/2==0){
        x=midvalues[groups/2];//中位数的中位数作为主元
    }else{
        x=midvalues[groups/2+1];//中位数的中位数作为主元
    }

    r=PARTITION(A,p,q,x)//设定主元的划分函数
    k=r-p+1;//小于A[r]的元素的个数的计算 
    if(i==k){
        return A[r]
    else if(i<k)
        SELECT(A,p,r-1,i)
    else
        SELECT(A,r+1,q,i)
    }
}
posted @ 2016-03-03 16:36  snowwolf101  阅读(226)  评论(0编辑  收藏  举报