1.理论
利用分治算法,即RANDDOMIZED-SELECT算法,这个算法是以7.2中的快速排序算法随机化版本为基础,将对输入数组进行递归划分,但和快速排序不同的是,快速排序会递归处理划分的两边,但RANDDOMIZED-SELECT只处理划分的一边。
伪算法:
在算法第3行的RANDOMIZED-PARTITION执行之后,数组A[p..r]被划分成两个(可能空的)子数组A[p..q-1]和A[q+1..r],使得A[p..q-1]中的每一个元素都小于或者等于A[q],进而小于A[q+1..r]中的每一个元素,与快速排序一样,称A[q]为主元素。RANDDOMIZED-SELECT的第4行计算数组A[p..q]内的元素个数k,即处于划分低区的元素的个数加上一个主元素。然后第5行检查A[q]是不是第i小的元素。如果是,就返回A[q]。否则,算法要确定第i小的元素落在两个子数组中A[p..q-1和A[q+1..r]中的哪一个中。如果是i<k,则要找的元素落在划分的高区子数组中,因为我们已经知道了有k个值(即A[p..q]中的所有元素)小鱼A[p..r]中的第i个元素,故所求元素必是A[q+1..r]中的第(i-k)小元素,它在第9行中被递归的选取。
2.C代码
/*************************************************************************
> File Name: randomIzedSelect.c
> Author: NULL
> Mail: 574889524@qq.com
> Created Time: 2014年11月02日 星期日 15时05分07秒
************************************************************************/
#include<stdio.h>
#include <stdlib.h>
#include <time.h>
#define ARRSIZE 8
/****************************************/
void ExChangeInt(int *a,int *b)
{
int iTemp = *a;
*a = *b;
*b = iTemp;
}
/****************************************/
/*快速排序核心代码*/
int Partition(int *A,int p,int r)
{
int j,x,i;
x = A[r];
i = p - 1;
for(j = p;j<=r-1;j++){
if(A[j] <=x){
i++;
ExChangeInt(&A[i],&A[j]);
}
}
ExChangeInt(&A[i+1],&A[r]);
return i + 1;
}
/*快排的随机主元素生成*/
int RandomIzedPartition(int *A,int p,int r)
{
int i;
srand((unsigned)time(NULL));
i =rand()%(r-p+1)+p;
ExChangeInt(&A[r],&A[i]);
return Partition(A,p,r);
}
/****************************************/
/*以期望线性时间选择第i小的元素
*pArr:查找的对象数组
*p:查找的起始位置
*r:查找的结束位置
*i:查找在区间[p,r]中第i小的元素
*/
int RandomIzedSelect(int *pArr,int p,int r,int i)
{
int k,q;
if(p==r)
return pArr[p];
q = RandomIzedPartition(pArr,p,r);/*得到主元素的下标*/
k=q-p+1;/*计算划分低区的元素个素*/
if(k == i)/*说明找到了第i小的元素*/
return pArr[q];
if(i<k)
return RandomIzedSelect(pArr,p,q-1,i);
else
return RandomIzedSelect(pArr,q+1,r,i-k);
}
/****************************************/
int main()
{
int Arr[ARRSIZE] = {1,2,3,4,5,6,7,8};
printf("i = %d\n",RandomIzedSelect(Arr,0,7,2));
printf("i = %d\n",RandomIzedSelect(Arr,1,7,2));
return 0;
}