O(n)时间求最接近中位数的k个数

算法导论 9.3-7 O(n)时间求最接近中位数的k个数

  279人阅读  评论(0)  收藏  举报

题目:

给出一个O(n)时间的算法,在给定一个有n个不同数字的集合S以及一个正整数k<=n后,它能确定出S中最接近其中位数的k个数

思考:

step1:求出数组的中位数的值O(n)

step2:计算数组每个数与中位数差的绝对值,存于另一个数组B中O(n)

step3:求出数组B中第k小的数ret O(n)

step4:计算数组S中与ret差的绝对值小于ret的数并输出O(n)

其中,step4也可以通过划分的方法找出数组S中与ret差的绝对值小于ret的数

代码:

[cpp]  view plain copy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. int length_A;  
  5. void Print(int *A)  
  6. {  
  7.     int i;  
  8.     for(i = 1; i <= length_A; i++)  
  9.         cout<<A[i]<<' ';  
  10.     cout<<endl;  
  11. }  
  12. /*************最坏情况线性时间的选择**************************************************/  
  13. //已经出现很多次了,不解释  
  14. int Partition(int *A, int p, int r)  
  15. {  
  16.     int x = A[r], i = p-1, j;  
  17.     for(j = p; j < r; j++)  
  18.     {  
  19.         if(A[j] <= x)  
  20.         {  
  21.             i++;  
  22.             swap(A[i], A[j]);  
  23.         }  
  24.     }  
  25.     swap(A[i+1], A[r]);  
  26.     return i+1;  
  27. }  
  28. int Select(int *A, int p, int r, int i);  
  29. //对每一组从start到end进行插入排序,并返回中值  
  30. //插入排序很简单,不解释  
  31. int Insert(int *A, int start, int end, int k)  
  32. {  
  33.     int i, j;  
  34.     for(i = 2; i <= end; i++)  
  35.     {  
  36.         int t = A[i];  
  37.         for(j = i; j >= start; j--)  
  38.         {  
  39.             if(j == start)  
  40.                 A[j] = t;  
  41.             else if(A[j-1] > t)  
  42.                 A[j] = A[j-1];  
  43.             else  
  44.             {  
  45.                 A[j] = t;  
  46.                 break;  
  47.             }  
  48.         }  
  49.     }  
  50.     return A[start+k-1];  
  51. }  
  52. //根据文中的算法,找到中值的中值  
  53. int Find(int *A, int p, int r)  
  54. {  
  55.     int i, j = 0;  
  56.     int start, end, len = r - p + 1;  
  57.     int *B = new int[len/5+1];  
  58.     //每5个元素一组,长度为start到end,对每一组进行插入排序,并返回中值  
  59.     for(i = 1; i <= len; i++)  
  60.     {  
  61.         if(i % 5 == 1)  
  62.             start = i+p-1;  
  63.         if(i % 5 == 0 || i == len)  
  64.         {  
  65.             j++;  
  66.             end = i+p-1;  
  67.             //对每一组从start到end进行插入排序,并返回中值,如果是最后一组,组中元素个数可能少于5  
  68.             int ret = Insert(A, start, end, (end-start)/2+1);  
  69.             //把每一组的中值挑出来形成一个新的数组  
  70.             B[j] = ret;   
  71.         }  
  72.     }  
  73.     //对这个数组以递归调用Select()的方式寻找中值  
  74.     int ret = Select(B, 1, j, (j+1)/2);  
  75.     //delete []B;  
  76.     return ret;  
  77. }  
  78. //以f为主元的划分  
  79. int Partition2(int *A, int p, int r, int f)  
  80. {  
  81.     int i;  
  82.     //找到f的位置并让它与A[r]交换  
  83.     for(i = p; i < r; i++)  
  84.     {  
  85.         if(A[i] == f)  
  86.         {  
  87.             swap(A[i], A[r]);  
  88.             break;  
  89.         }  
  90.     }  
  91.     return Partition(A, p, r);  
  92. }  
  93. //寻找数组A[p..r]中的第i大的元素,i是从1开始计数,不是从p开始  
  94. int Select(int *A, int p, int r, int i)  
  95. {  
  96.     //如果数组中只有一个元素,则直接返回  
  97.     if(p == r)  
  98.         return A[p];  
  99.     //根据文中的算法,找到中值的中值  
  100.     int f = Find(A, p, r);  
  101.     //以这个中值为主元的划分,返回中值在整个数组A[1..len]的位置  
  102.     //因为主元是数组中的某个元素,划分好是这样的,A[p..q-1] <= f < A[q+1..r]  
  103.     int q = Partition2(A, p, r, f);  
  104.     //转换为中值在在数组A[p..r]中的位置  
  105.     int k = q - p + 1;  
  106.     //与所寻找的元素相比较  
  107.     if(i == k)  
  108.         return A[q];  
  109.     else if(i < k)  
  110.         return Select(A, p, q-1, i);  
  111.     else  
  112.         //如果主元是数组中的某个元素,后面一半要这样写  
  113.         return Select(A, q+1, r, i-k);  
  114.         //但是如果主元不是数组中的个某个元素,后面一半要改成Select(A, q, r, i-k+1)  
  115. }  
  116. int main()  
  117. {  
  118.     int k, i;  
  119.     while(cin>>length_A>>k)  
  120.     {  
  121.         if(k > length_A)  
  122.         {  
  123.             cout<<"error:k > length_A"<<endl;  
  124.             continue;  
  125.         }  
  126.         //生成随机数据  
  127.         int *A = new int[length_A+1];  
  128.         for(i = 1; i <= length_A; i++)  
  129.             A[i] = rand() % 100;  
  130.         Print(A);  
  131.         //计算中位数  
  132.         int mid = Select(A, 1, length_A, (length_A+1)/2);  
  133.         //计算每个数与中位数的距离  
  134.         int *B = new int[length_A+1];  
  135.         for(i = 1; i <= length_A; i++)  
  136.             B[i] = abs(A[i] - mid);  
  137.         //选择第k小的数  
  138.         int ret = Select(B, 1, length_A, k);  
  139.         //求出数组是与中位数距离小于ret的数,并输出  
  140.         for(i = 1; i <= length_A; i++)  
  141.             if(abs(A[i] - mid) <= ret)  
  142.                 cout<<A[i]<<' ';  
  143.         cout<<endl;  
  144.         delete []A;  
  145.     }  
  146.    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值