题目描述:对一个包含n个元素的集合来说,k分位数是指能把有序集合分成k个等大小集合的第k-1个顺序统计量。给出能找出某一集合的k分位数的O(nlgk)时间的算法。
k分位数解释:
令每个子集合的元素个数为t = n / k,A[j]是数组A中下标为j的元素,A(j)是数组是第j大的元素
则所求的k分位数是指A(t),A(2t),A(3t),……,A((k-1)t)
直接搜索k次达不到时间要求,可采用如下思路求解:
1、先找A(t),A(2t),A(3t),……,A((k-1)t)中间的第kt/2个顺序统计量;
2、第一步查找结束后,数组分成前后两半,接着依次处理这两个子数组。
代码如下:(只考虑了n是k的整数倍的情况)
#include <iostream>
using namespace std;
void exchange(int &a, int &b)
{
int temp;
temp = a;
a = b;
b = temp;
}
int partition(int A[],int p, int r, int key)
{
int i = p - 1,j;
for(j=p;j<r; ++j)
if(A[j]==key)
break;
exchange(A[j],A[r]);
for(j=p; j<r; ++j)
{
if(A[j]<=key){
i++;
exchange(A[i],A[j]);
}
}
exchange(A[i+1],A[r]);
return i+1-(p-1);
}
void insert_sort(int A[],int p, int r)
{
if(p>=r)
return;
int key,i,j;
for(j=p+1;j<=r; j++)
{
key = A[j];i=j-1;
while(i>=p && A[i]>key)
{
A[i+1] = A[i];
i = i-1;
}
A[i+1] = key;
}
}
int select(int A[],int p, int r, int i)
{
if(p==r)
return A[p];
int k = p+4;
while(k<=r)
{
insert_sort(A, k-4, k);
k += 5;
}
insert_sort(A, k-4, r);
int num;
num = (r-p+1)/5 + ( (r-p+1)%5 ? 1:0 );
int *new_arr = new int[num];
for(int j=0;j<num-1;++j)
new_arr[j] = A[3+j*5+p-1];
//找出最后一个中位数
k = r-(num-1)*5 - (p - 1);
if(k%2)k = k + 1;
k = k/2;
k += (num-1)*5 + p - 1;
new_arr[num-1] = A[k];
//对[n/5]个中位数进行插入排序,找出中位数的中位数x
insert_sort(new_arr, 0, num-1);
int x = new_arr[(num-1)/2];
delete new_arr;
//按中位数x对输入数组进行划分
k = partition(A,p,r,x);
if(k==i)
return x;
else if(k>i)
select( A, p, k-1 + (p-1), i);
else
select( A, k+1 + (p-1), r, i-k);
}
void find_k(int A[],int p, int r, int k)
{
if(k<1)return;
int len = (r-p+1)/k;
int i;
i = k/2;
if(i>0){
select(A,p,r,i*len);
find_k(A,p,i*len+(p-1),k/2);
find_k(A,i*len+p,r,k%2?k/2+1:k/2);
}
}
void test()
{
int A[] = {0,5,15,14,13,12,9,10,7,3,2,4,6,8,1,11,17,16,18,20,19};
find_k(A,1,20,5);
for(int i=1;i<5; ++i)
cout<<A[i*4]<<" ";
cout<<endl;
}
int main()
{
test();
return 0;
}