问题描述:
给定由n个互补相同的数组成的集合S以及正整数k<=n,试设计一个O(n)时间算法找出S中最接近S的中位数的k个数。
算法分析:
首先,通过select算法得到这个数组的中位数,然后最这个集合中的每一个数都减去这个中位数然后取abs();最后在第二步得到的集合中,取第k小的数,再取小于第k小的数的k-1个数,他们原来的数,就是要求的k个数。PS:此算法有缺陷,要求保证绝对值数组中无重复,否则无法线性。
转自:http://hi.baidu.com/cosersoft/item/d6fba1cc9d40493298b4989c
算法实现:
#include <iostream>
using namespace std;
const int ArraySize=7;
const int Array[ArraySize]={2,63,11,23,434,333,1};//原始数组
int a[ArraySize];//查找数组
int b[ArraySize];
int Select(int temp[],int p , int r , int k);//选择在数组temp中,第k大的数字
int partition(int p ,int r);
void absArray(int midNum);
int main()
{
for (int j=0;j<ArraySize;j++)
{
a[j]=Array[j];//从原始数组copy数据
}
int k;
cout<<"Default Array is : {2,63,11,23,434,333,1}."<<endl;
cout<<"Please input the number k :"<<endl;
cin>>k;
int mid=(ArraySize+1)/2;
int numMid=Select(a,0,ArraySize-1,mid);//得到中位数
cout<<"中位数是 :"<<numMid<<endl;
absArray(numMid);//生成绝对值数组
int kNum=Select(b,0,ArraySize-1,k);
cout<<"最接近的 "<<k<<" 个数为:";
for (int i=0;i<ArraySize;i++)
{
if(b[i]<=kNum) cout<<Array[i]<<" ";
}
cout<<endl;
return 0;
}
int partition(int temp[],int p ,int r)
{
int i=p-1;
int j=r+1;
int x=temp[p];
while(true)
{
while (temp[++i]<x && i<r);
while(temp[--j]>x);
if (i>=j) break;
int tempNum=temp[i];
temp[i]=temp[j];
temp[j]=tempNum;
}
temp[p]=temp[j];
temp[j]=x;
return j;
}
int Select(int temp[],int p , int r , int k)
{
if(p==r) return temp[p];
int i=partition(temp,p,r);
int j=i-p+1;
if (k<=j)
{
return Select(temp,p,i,k);
}
else return Select(temp,i+1,r,k-j);
}
void absArray(int midNum)
{
for(int i=0;i<ArraySize;i++)
{
b[i]=abs(Array[i]-midNum);
}
}