编程之美系列之寻找最大的K个数

题目描述:有很多无序的数,姑且家丁它们各不相等,怎么选出其中最大的若干个数呢?这里我不想去写一些很没有意义的思路。神马先排序取前k个这种弱爆了并且一点也不适用的思想我就不想废话了,因为如果数据量很大的时候,对所有数据排序肯定是费力不讨好的事情,换换思路,不能全部排序,那就部分排序吧!这里介绍两个比较实用的。当然为了有点递进关系,先来一个比较差一点的方法。如果对排序算法还不是很熟悉的,可以参考博客: 排序算法
1、快排。回忆一下快排的思想,选一个关键元素key,利用key将数组分成两部分,左边一部分比key大,右边一部分比key小(当然本身的定义是左边一部分比key小,右边一部分比key大),那么key的位置有下面三种情况:
*如果key正好放在第k个数的位置上面,OK,那说明前k大的数我们已经找到了,就不需要排序了。
*如果key的位置比k小,那说明前面找到的最大的元素中还不足k个,那就在后面一段的数据中继续找。
*如果key的位置比k小,那说明前面找到的最大的元素的个数超过k了,那就在前面的一段数据中分出k个就不可以了。
如果是需要找最小的k个元素,类似的思想,这里就不废话了。代码如下:

void QuickSort(int *arr, int Begin, int End, int k)
{
	if(!arr || Begin >= End || End < 0)
		return;
	int i,j;
	int key;
	i = (double)rand()/RAND_MAX * (End - Begin + 1) + Begin;
	key = arr[i];
	arr[i] = arr[Begin];
	arr[Begin] = key;
	i = Begin;
	j = End;
	while(i < j)
	{
		while(i < j && arr[j] <= key)//找到后面第一个比key大的元素
			--j;
		if(i < j)
			arr[i++] = arr[j];
		while(i < j && arr[i] >= key)//找到前面第一个比key小的元素
			++i;
		if(i < j)
			arr[j--] = arr[i];		
	}
	arr[i] = key;
	if(i == k - 1)//恰好k个,返回
		return;
	else if(i < k - 1)//不足k个,在后面继续找
		QuickSort(arr, i + 1, End, k);
	else//多于k个,在前面分出k个
		QuickSort(arr, Begin, i - 1, k);
}

2、堆排序。如果数据量很多的时候,快排有个什么坏处呢?它需要将所有数据都一次导入内存,在海量数据的情况下,显然是不能做到的,但是堆排序就可以。如果我们需要找最大的k个数,那就维护一个由k个数组成的最小堆,堆顶元素是堆的最小值。每次进来一个数,先与堆顶元素比较,如果小于堆顶元素,那一定小于堆中的任何一个元素。如果大于堆顶元素,OK,用这个数替换堆顶元素,然后调整新得到的堆。代码如下:

//将开始元素s到nLen组成的堆调整为最小堆
void MinHeapAdjust(int *arr, int s, int nLen)
{
	if(!arr || s < 1 || nLen < 1 || s > nLen)
		return;
	int i,t;
	t = arr[s];
	for(i = s<<1; i <= nLen; i = s<<1)
	{
		if(i < nLen && arr[i] > arr[i|1])//找到左右子树中最小的一个
			i |= 1;
		if(t < arr[i])//t已经很小了,就不往下沉了
			break;
		arr[s] = arr[i];
		s = i;
	}
	arr[s] = t;
}

3、给出两种方法的main函数调用,如果不需要,可以直接pass掉。

//打印数组
void Print(int *arr, int nLen)
{
	for(int i = 0; i < nLen; ++i)
		printf("%d ", arr[i]);
	printf("\n");
}
int main()
{
	const int N = 30;
	int arr[N];
	int i,n,k,v;
	/* //快排的main函数调用部分
	while(scanf("%d %d", &n, &k) != EOF)
	{
		for(i = 0; i < n; ++i)
			scanf("%d", &arr[i]);
		printf("输入k:");
		scanf("%d", &k);
		QuickSort(arr, 0, n - 1, k);
		Print(arr, k);
	}
	*/
	//堆排序main函数的调用部分
	while(scanf("%d %d", &n, &k) != EOF)
	{
		for(i = 1; i <= k; ++i)
			scanf("%d", &arr[i]);
		//得到最小堆
		for(i = k>>1; i > 0; --i)
			MinHeapAdjust(arr, i, k);
		for(i = k; i < n; ++i)
		{
			scanf("%d", &v);
			if(v > arr[1])
			{
				arr[1] = v;
				MinHeapAdjust(arr, 1, k);
			}
		}
		Print(&arr[1], k);
	}
}




原文地址:https://www.cnblogs.com/javawebsoa/p/3053076.html