首页 > 代码库 > 算法题:求数组中最小的k个数

算法题:求数组中最小的k个数

       说明:本文仅供学习交流,转载请标明出处,欢迎转载!

        题目:输入n个整数,找出其中最小的k个数

       《剑指offer》给出了两种实现算法:

         算法1:采用Partition+递归法,该算法可以说是快速排序和二分查找的有机结合。算法的时间复杂度为O(n),缺点在于在修改Partition的过程中会修改原数组的值。

         算法2:采用top-k算法。如果要找最小的K个数,我们才用一个含有K个值的大顶堆;如果要找最大的K个数,我们采用小顶堆。该算法的时间复杂度为O(nlogK),是一种比较好的算法,启发于堆排序。

         这里我说明下《剑指offer》在实现上我认为不足的地方:书中用一个基于红黑树的multiset容器来模拟堆,但实际上红黑树是一棵BST,而堆不一定满足BST的特征。虽然用红黑树能得到结果,但是我认为并不是最好的,其实STL中有一个容器适配器priority_queue,其底层的实现数据结构就是一个堆,由于堆是一棵完全二叉树,所以数组的下标与标准定义的左右孩子相对应,所以用基于vector容器的priority_queue的大顶堆我们能够获取最小的K个数。

         我的实现代码(用priority_queue):

int* GetMinK(int *arr,int n,int k,int *Result)//用priority_queue实现一个堆
{
	if(!arr || n<1 || k<1 || k>n || !Result)
		return NULL;
	priority_queue<int,vector<int>,less<int> > q;//建一个大顶堆(less),小顶堆(greater)
	int i;
	for(i=0;i<k;i++)//先压入k个元素
	{
		q.push(arr[i]);
	}
	int max=q.top();//获取堆顶元素
	for(i=k;i<n;i++)
	{
		if(arr[i]<max)//如果当前元素比堆顶元素还小
		{
			q.pop();//将堆顶元素删除
			q.push(arr[i]);//并将当前元素压入到堆中
			max=q.top();//更新max值(栈顶元素值)
		}
	}
	i=0;
	while(!q.empty())//将堆中每一个元素的值从堆中弹出,并放入到输出数组中
	{
		Result[i++]=q.top();
		q.pop();
	}
	return Result;
}

         《剑指offer》中用multiset容器实现的代码:

#include<iostream>
#include<set>//用到deque容器
#include<iterator>//用到输出迭代器
#include<algorithm>//用到copy函数
#include<functional>//用到仿函数
using namespace std;

int*  GetMinK(int *arr,int n,int k,int *Result)//用红黑树实现
{
	if(!arr || n<1 || k<1 || k>n || !Result)
	{
		cout<<"Invalid Input!";
		return NULL;
	}
	multiset<int,greater<int> >mset(arr,arr+k);//用降序排序
	int i=k;
	multiset<int,greater<int> >::iterator iter;
	for(;i<n;i++)
	{
		iter=mset.begin();
		if(arr[i]<*iter)//如果当前元素小于map中的最大值
		{
			mset.erase(iter);
			mset.insert(arr[i]);
		}
	}
	i=0;
	while(!mset.empty())//将集合中的元素复制到输出数组中后删除
	{
		iter=mset.begin();
		Result[i++]=*iter;
		mset.erase(iter);
	}
	return Result;
}
          测试代码:

int main()
{
	int arr[]={1,9,4,3,2,5,6,7,8};
	int n=sizeof(arr)/sizeof(int);
	cout<<"原数组的元素为:";
	copy(arr,arr+n,ostream_iterator<int>(cout," "));
	cout<<endl;
	cout<<"请输入k值:";
	int k;
	while(cin>>k)
	{
		int *result=new int[k];
		result=GetMinK(arr,n,k,result);
		if(result)
		{
			copy(result,result+k,ostream_iterator<int>(cout," "));
		}
		cout<<endl;
		delete []result;
	}
	return 0;
}

          测试结果如下:

                   

参考文献------------《剑指offer》