利用快速排序的思想查找第k小的数,主要的核心思想还是快速排序的分治算法,具体的思路是这样的:
1 利用快排的partion函数将数组分成左右两个部分,
2 如果p刚好等于k,则说明p位置的数,就是我们要找的数,如果分出来的边界位置p小于给定的数k,我们知道最小的第k个数,肯定在p的右边,如果p大于给定的k则在p边界的左边
3 递归在p的左边或者右边查找
注:p为数组下标需要加1,一般会使用随机数,即在数组中随机选取一个数作为每次分边界的值。
具体的细节可以查看《算法导论》第九章,下面是简单的实现代码,没有完全验证过;
/*
查找第k小的数
*/
#include <iostream>
using namespace std;
int partion(int array[],int s,int e)
{
int temp = array[s];
while(s < e)
{
while(array[e] >= temp&& s<e )
e--;
if(s < e)
array[s] = array[e];
while(array[s] < temp && s<e)
s++;
if(s < e)
array[e] = array[s];
}
array[s] = temp;
return s;
}
int search_k(int array[],int s,int e,int k)
{
int p = partion(array,s,e);
int q = p+1;
if( q == k )
return array[p];
if( q < k)
return search_k(array,p+1,e,k);
if( q > k)
return search_k(array,s,p-1,k);
}
int main()
{
int array[1000];
int n;
int k;
while( cin >> n >> k && n!=0)
{
for(int i=0;i<n;i++)
cin >> array[i];
cout << search_k(array,0,n-1,k) << endl;
}
}
查找最小的k个数,我用上面的方法肯定也是可以的,只需要将分界的p返回,p左边的数(包括p位置本身),就是我们需要的所有的最小的k个数,简单验证过,还可以维护一个集合数据的个数为k,每次从数组中取出一个数,如果集合中的数字的个数小于k,则认为集合中的所有数都是最小的k个数,如果集合中的的数据的个数大于k了,则每次将最大的数置换出来,遍历完数组之后,集合中的k个数就是最小的k个数的集合,用c++ 中的STL可以简单实现下面的代码:
#include <iostream>
#include <set>
#include <vector>
using namespace std;
typedef multiset<int,greater<int> > IntHeap;
void FindLeastKNum(vector<int> &ivec,
int k,
IntHeap &leastNum)
{
for(vector<int>::iterator iter= ivec.begin();
iter != ivec.end(); iter++)
{
// cout << *iter << " ";
if(leastNum.size() < k)
leastNum.insert(*iter);
else
{
IntHeap::iterator Tmpiter = leastNum.begin(); //
// cout <<"the biggest"<< *Tmpiter <<endl;
if(*iter < *Tmpiter)
{
leastNum.erase(Tmpiter);
leastNum.insert( *iter );
}
}
}
}
int main()
{
int n;
cin>> n ;
vector<int> ivec;
while(n--)
{
int num =0;
cin >> num;
ivec.insert(ivec.begin(),num);
}
int k;
cin >> k;
IntHeap leastNum;
FindLeastKNum(ivec,k,leastNum);
cout << "the least k nums are: " <<endl;
for(IntHeap::iterator iter=leastNum.begin();
iter != leastNum.end(); iter++)
cout << *iter << " ";
cout <<endl;
}