求k分位数的k-1个顺序统计量

问题:对于一个包含n个元素的集合来说,k分为数就是指能把有序集合分成k个等大小集合的“k-1个顺序统计量”,给出一个能找出某一集合的k分位数的O(nlogk)的算法。

       首先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(nlogk).

#include<iostream>
#include<algorithm>
using namespace std;
void Select_K(int *a, int p, int r, int k);
int main(){
	/*测试*/
	int i;
	int const n = 9;
	const int k = 3;
	int a[n] = { 8, 4,0, -89, -12, 0, 36, 789, 21};
	Select_K(a, 0,9,k);
	for (i =1; i < k; i++)
		cout << a[n/k*i-1] << endl;
	return 0;
}
int Partition(int a[],int low,int high, int x){  //这里数组a是[low,high)的,注意右边界最多到a[high-1],
	/*利用x将数组划分为2部分*/
	int i = low;
	high--;
	while (a[i]!= x) i++;
	swap(a[low], a[i]);  //将基准移到首位置
	while (low < high){
		while (low < high&&a[high] >= x) high--;
		a[low] = a[high];
		while (low < high&&a[low] <= x) low++;
		a[high] = a[low];
	}
	a[low] = x;
	return low; 
}
int Select(int *a, int low, int high, int k){
	int i, j,x,q,n;
	n = high - low;                                //n为数组a[low...high]元素个数,注意右边最多取到a[high-1]
	if (n < 5){      //元素小于5时候单独处理
		sort(a + low, a + high);
		return a[low +k-1];
	}
	for (i = 0; i <n/ 5; i++){
		sort(a + low+i * 5, a + low+i * 5 + 5);   //对每组数据排序
		swap(a[low+i], a[low+i * 5 + 2]);        //中位数移到前面
	}
	x = Select(a, low, low + n / 5, n / 10 + 1);          //寻找中位数的中位数、n/10+1非常重要,避免n<10时n/10==0此时会出现问题
	j = Partition(a, low, high, x);             //根据x将数组a划分为2部分,j为x所在数组下标
	q = j - low + 1;                           // q为小于或者等于x元素的个数
	if (q == k)
		return x;
	else if (q>k)
		return Select(a, low, j + 1, k);
	else
		return Select(a, j + 1, high, k - q);
}
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);
}


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值