【数据结构与算法】寻找最大的K个数

腾讯一面遇到这个题,发现这个题真的是比较经典,打算在这里好好总结一下。

参考自师姐的博文:http://blog.csdn.net/xiaxia__/article/details/44965455(XIAXIA_的专栏)


题目简介:

有很多无序的数,我们姑且假定他们各不相等,怎么选出其中最大的K个数呢?




解法一:直接排序


第一反应,假设有N个数,我们使用一个N个长度的数组将其存储下来,并且使用排序算法将其从大到小依次排列。排序完成后,输出前K个数。如果N不小,但是也不大,比如几千什么的,可以采用快速排序来完成。


复杂度分析: 
快速排序平均的复杂度为O(NlogN)。





解法二:部分排序


简单分析一下,我们就能发现解法一的一个明显不足之处,那就是我们将所有的元素都进行了排序,而题目要求只是寻找最大的K个数,也就是说我们只要将最大的K个数排好序就好了,没必要将剩下的N-K个数也进行排序。


在这里,我们可以使用快速排序来完成这个部分排序的功能。在快速排序中,每一轮都需要选定一个pivot,每一轮排序完成后,比pivot大的数都排在它前面,而比pivot小的数都排在它的后面。假设前面的序列为Sa,后面的序列为Sb,Sa的长度为n。

此时有三种情况:

1)当n>K时,我们直接输出Sa的前K个元素就好了; 
2)当n=K时,我们直接输出Sa这个序列; 
3)当n<K时,我们就需要从Sb中找出K−n个元素和Sa一起输出就好了。


完整测试代码如下:

#include <iostream>  
#include <ctime>
#include <cstdlib>
using namespace std;  

void kBig(int *pArray, int low, int high, int K);
int partion(int *pArray, int low, int high);

int main()  
{  
	srand((unsigned)time(NULL));
   	int data[50];
	int K = 3, i;

	for(i = 0; i < 50; i++)
	{
		data[i] = rand() % 200;
		cout << data[i] << " ";
	}
	cout << endl;

        kBig(data, 0, 50 - 1, K);
	for (i = 0; i < K; i++)
            cout << data[i] << endl;

	return 0;
}  

//将前K大个数移到数组前K个位置上
void kBig(int *pArray, int low, int high, int K)
{
    int index, n;
    if (low <= high)
    {
        //对数组进行划分,并返回划分的位置
        index = partion(pArray, low, high);
        n = index - low + 1;     			        //Sa的个数
        
	if (n == K)     				        //如果恰好是K个的话,那么返回
            return;
        
	if (n < K)     						//如果Sa的个数不够的话,那么再从Sb中找K-n个
            kBig(pArray, index + 1, high, K - n);

        if (n > K)     						//如果Sa的个数大于K的话,那么就从Sa里面返回K个
            kBig(pArray, low, index, K);
    }
}

//快速排序的划分函数并返回pivot的坐标
int partion(int *data, int left, int right)
{
	if(left >= right)
		return left;

	int pivot;
	pivot = data[left];
	while(left < right)
	{

		while(right > left && data[right] <= pivot)
			right--;
		data[left] = data[right];

		while(right > left && data[left] >= pivot)
			left++;
		data[right] = data[left];
	}	
	data[left] = pivot;

    return left;
}

复杂度分析:


复杂度为O(N)。





解法三:堆排序


如果N是一个较大的数,那用这么大的数组来存储并进行快排,这就是非常不明智地做法了。此时我们可以使用一个大小为K的最小堆来完成。完整测试代码如下:

#include <iostream>  
#include <ctime>
#include <cstdlib>
using namespace std;  

void PercDown(int *data, int i, int N)
{
	int tmp, child;
	child = i << 1;
	for(tmp = data[i]; child <= N;)
	{
		if(child + 1 <= N && data[child + 1] < data[child] )
			child++;
		if(data[child] >= tmp)
			break;
		
		data[i] = data[child];
		i = child;
		child = i << 1;
	}
	data[i] = tmp;
}

void BuildHeap(int *data, int N)
{
	int i;
	for(i = N / 2; i > 0; --i)
		PercDown(data, i, N);
}

int main()  
{  
	srand((unsigned)time(NULL));
   	int data[50];
	int sortheap[11] = {0};
	int K = 10, i;

	for(i = 0; i < 50; i++)
	{
		data[i] = rand() % 200;
		if(i < 10)
			sortheap[i + 1] = data[i];
		cout << data[i] << " ";
	}
	cout << endl;

	BuildHeap(sortheap, 10);                                 //建立大小为K的最小堆
	for(i = 10; i < 50; ++i)
	{
		if(data[i] > sortheap[1])                        //遍历剩下的元素,如果大于根节点就赋值到根节点,然后下滤
		{
			sortheap[1] = data[i];
			PercDown(sortheap, 1, 10);
		}
	}

	for (i = 1; i < 11; i++)
        cout << sortheap[i] << " ";
	cout << endl;
	
	return 0;
}  


复杂度分析:O(NlogK)可以看到堆排序这种做法并没有怎么提高时间复杂度,但是却极大的降低了对空间的存储要求,只需要维护一个K大小的堆。


对于堆排序,需要牢记的是 下滤(PercDown) 这个函数,有了这个函数,我们可以轻易实现 BuildHeap,DeleteMin的操作。此外,PercDown 这个函数还很契合我们当前的这个用法。






解法四:计数排序法


适用情况:如果所有N个数都是正整数,且他们的取值范围不大,我们知道最大的数是MAXN。那么我们可以申请一个数组count[MAXN] 来记录每个数出现的次数。然后我们就可以找出最大的K个数。

复杂度:O(N)

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值