《剑指offer》:[30]最小的K个数问题

题目:输入N个整数,找出其中最小的k个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4.
方案一:就是先对数组进行排序,然后输出前K个数字。时间复杂度为O(N*logN)
方案二:根据[29]中数字超过一半的启发,我们可以用基于快速排序的思想,对其进行划分 ,时间复杂度为O(N)。找出前K小个元素,我们只需要利用Partion函数将小于X的前k个元素移动到X的前面就可以了。顺序输出这K个元素就是所求,可能不是有序的。
方案三:O(N*logN)的算法。适合处理海量的数据。
         我们可以创建一个大小为K的容器来存储最小的K个数,接下来我们每次从输入的N个整数中读入一个数。如果容器中已有的数字少于K个,我们直接把这次读入的整数放入容器中;如果容器已满,也就是说容器中已有K个元素,此时我们不能在插入新的数字而只能是替换已有的数子。找出已有数据中的最大的数据,如果待插入的数据比该数据还大,则不可能是前K个最小的元素,舍弃;反之,则将其与最大元素进行替换。
        容器满了以后我们要做三件事:第一,在K个元素中找到最大的元素;第二,有可能在这个容器中删除最大数;第三,有可能插入一个新的数据。如果用一个二叉树来实现该数据容器,则我们可以在O(logK)的时间内完成寻找,插入,删除操作。对于输入的N个元素,总的时间效率就是 O(N*logK)。例如最大堆和二叉树都是可以的。
      这里如果可以用STL,我么不妨选择set和multiset来完成,因为它们都是基于红黑树实现的,严格控制了操作时间复杂度为O(logN)。
      但是set和multiset的区别是:set插入的元素不能相同,但是multiset可以相同。
      注意;方案二是会修改原数据的位置的,但是方案三不会修改,并且适合处理海量的数据。它是将数据读出来,在容器里操作。
方案二实现代码:
#include <iostream>
using namespace std;
int arr[8]={4,5,1,6,2,7,3,8};
void Swap(int &a,int &b)
{
	int temp=a;
	a=b;
	b=temp;
}
int Partion(int *array,int length,int low,int high)
{
	while (low <high)
	{
		while(low<high && array[high]>=array[low])
			high--;
		Swap(array[high],arr[low]);
		while(low < high && array[low]<=array[high])
			low++;
		Swap(array[low],array[high]);
	}
	return low;
}
int GetLeastNumbers(int *input,int *output,int k,int length)
{
	if(NULL==input ||NULL==output || length<=0 || k>length || k<=0)
		return 0;
	int low=0;
	int high=length-1;
	int index=Partion(input,length,low,high);
	while(index!=k-1)
	{
		if(index>k)
		{
			high=index-1;
			index=Partion(input,length,low,high);
		}
		else
		{
			low=index+1;
			index=Partion(input,length,low,high);
		}
	}
	for(int i=0;i<k;i++)
		output[i]=input[i];
}
int main()
{
	int *result=new int[4];
	GetLeastNumbers(arr,result,4,8);
	cout<<"最小的4个数是:";
	for(int i=0;i<4;i++)
		cout<<result[i]<<" ";
	delete []result;
	cout<<endl;
	system("pause");
	return 0;
}

运行结果为:


方案三实现代码:
#include <iostream>
#include <vector>
#include <set>
using namespace std;
int arr[8]={4,5,1,6,2,7,3,8};
vector<int > v1(arr,arr+8);
vector<int>::iterator it;
multiset<int,greater<int>> intSet;
multiset<int,greater<int>>::iterator itset;
//这个对象里面存贮的元素是从小到大排序的,(因为用std::less作为比较工具);
//如果是最大的元素:则是std::greater
int GetLeastNumbers(const vector<int> & vec,multiset<int,greater<int>> &leastNumbers,int k)
{
	leastNumbers.clear();
	if( k<=0|| vec.size()<k)
		return 0;
	vector<int>::const_iterator it1=vec.begin();
	for(;it1!=vec.end();it1++)
	{
		if(leastNumbers.size()<k)
			leastNumbers.insert(*it1);
		else
		{
			itset=leastNumbers.begin();
			if(*it1<*(leastNumbers.begin()))
			{
				leastNumbers.erase(itset);
				leastNumbers.insert(*it1);
			}
		}
	}
}
int main()
{
	int i=0;
	GetLeastNumbers(v1,intSet,4);
	cout<<"原数据:";
	for(it=v1.begin();it!=v1.end();it++)
		cout<<*it<<" ";
	
	cout<<endl<<"前4小的数据:";
	for(itset=intSet.begin();itset!=intSet.end();itset++)
		cout<<*itset<<" ";
	cout<<endl;
	system("pause");
	return 0;
}

运行结果:




  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值