求数组中最小的k个数

这篇博客探讨了如何在一组整数中找出最小的k个数。提出了两种解决方案,一种是通过排序,时间复杂度为O(nlogn),另一种是利用快速排序的partition思想,使用数据容器(如最大堆或红黑树实现的multiset)在O(n)的时间复杂度内找到结果。

1、题目

输入n个整数,找出其中最小的k个数,例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4.

2、分析思路

思路1:最简单的莫过于先将数组排序,排序完之后最前面的k个数就是最小的k个数。时间复杂度是O(nlogn)。

思路2:时间复杂度o(n),只有当我们可以修改输入的数组时可用

我们从快速排序基于partition函数来解决这个问题。如果基于数组的第k个数字来调整,使得比第k个数字小的所有数字都位于数组的左边,比第k个数字大的所有数字都位于数组的右边。这样调整之后,位于数组中左边的k个数字就是最小的k个数字(但这k个数字不一定是排序的)。

参考代码(剑指offer)

void GetLeastNumbers(int* input, int length, int* output, int k)
{
	if(input == NULL || length == 0 || output == NULL || k > length || k<=0)
		return;
	int start = 0;
	int end = length-1;
	int index=partition(input,start,end,length);
	while(index != k-1)
	{
		if(index > k-1)
		{
			end = index-1;
			index = partition(input,start,end,length);
		}
		if(index < k-1)
		{
			start = index+1;
			index = partition(input,start,end,length);
		}
	}

	for(int i=0;i<k; i++)
		output[i]=input[i];
}

思路3:O(nlogk),适合处理海量数据

先创建一个大小为k的数据容器来存储最小的k个数字,接下来每次从输入的n个整数中读入一个数。

如果容器中已有的数字少于k个,则直接把这次读入的整数放入容器之中;

如果容器之中已有k个数字了,此时我们不能再插入新的数字而只能替换已有的数字。找出这已有的k个数中的最大值,然后拿这次待插入的整数和最大值进行比较。

如果待插入值比当前已有的最大值小,则用这个数替换当前已有的最大值;

如果待插入值比当前已有的最大值还大,那么这个数不可能是最小的k个数之一,所以可以直接抛弃;

因此当容器满了之后,我们要做3件事情:一是在k个整数中找到最大数;二是有可能在这个容器中删除最大数;有可能插入一个新的数字;可以考虑使用二叉树来实现这个数据容器,那么我们能在O(logk)的时间内实现这三步操作。

最大堆可以很容易在O(1)时间内找到k个数中的最大值,删除及插入操作需要O(logk)时间。但是由于实现一个最大堆需要一定的代码,我们还可以采用红黑树来实现数据容器。在STL中set和multiset都是基于红黑树实现的。所以可以考虑用STL中的multiset来完成。

参考代码

typedef multiset<int, greater<int> > intSet;  //greater<T>内置比较函数
typedef multiset<int, greater<int> >::iterator setIterator;
void GetLeastNumbers2(const vector<int>& input, intSet& output, unsigned int k)
{
	output.clear();
	if(k<1 || (input.size()) < k)
		return;
	vector<int>::const_iterator iter=input.begin();
	for(;iter != input.end(); ++iter)
	{
		if(output.size() < k)
		{
			output.insert(*iter);
		}
		else
		{
			setIterator setIter=output.begin();
			if(*iter < *(output.begin()) )
			{
				output.erase(*setIter);
				output.insert(*iter);
			}
		}
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值