求k分位数的k-1个顺序统计量
2016-02-28 16:59
661 查看
问题:对于一个包含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).
首先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 = { 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); }
相关文章推荐
- 使用Python做科学计算初探(转)
- 动态更换view类的背景----StateListDrawable的应用
- ios 第三方qq授权登陆,第一次登陆后,再次登陆,失效
- SOUI入门
- C++ wchar_t 输出中文问题
- LightOJ 1031 - Easy Game【区间dp】
- python 面向对象(进阶篇)
- Android传感器计步器
- OA学习笔记-002-Sruts2.1配置
- POJ2063 Investment (完全背包)
- Longest Consecutive Sequence
- 最小二乘拟合(转)good
- poj2305 Basic remains
- maven下面编译失败,失败提示信息为:程序包com.sun.image.codec.jpeg不存在
- 创建Sql Server CLR集成
- Java 并发变成同步机制
- android获取设备屏幕大小的方法
- mac终端下运行shell脚本
- SQL基本语法(mysql)
- 51nod 1055最长等差数列 dp