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

来源:互联网 发布:公司网络电脑管理制度 编辑:程序博客网 时间:2024/05/22 14:08

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

参考自师姐的博文: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)

0 0
原创粉丝点击