题干
20个有序数组,每个数组有500个数字,取出这10000个数字中最大的500个,怎么做?
我的做法
- 蛮力法:将这个10000个数组,放入一个数组内,然后排序,取出前500个。
- 写一个方法,将两个数组,将两个数组整合到一个数组里面,排序,前500个就是我们需要的,再反复调用这个方法。具体来说:有一个MAX先装满0,然后它先与A[1]装在一个1000的数组内后,排序,把前500个再复制给Max,然后再让MAX与A[2]重复上述过程,一直到A[19];
2个方法都很蠢。最好的做法:
见:http://zhidao.baidu.com/link?url=KU9AwrgsUsCXWMO7oOLTpt6WW58dHZRfQ36wyxsBYwdeSF07C-Pe6sjkrKwQnyGDoYUe_9QD9PKlu0bSGS9LPK
再用自己的话叙述一遍:
先作一个结构体:结构体内2个元素,一个是具体的数值,另一个是这个数值的来源。
然后从20个数组,分别取出最大的一个数,以结构体的形式,放入一个大小为20的数组。
对这个大小为20的数组进行排序(实际上这里如果使用最大堆的话,是不一定非要排序的,只需要完成建立最大堆的过程即可,当建立的完成最大堆后,最大元素就是需要被取走的元素,所以每次只需要调整一次堆即可,取出最大的一个数,放入大小为500的集合(这个集合装有最终的结果)。
从被取走的那个数的数值来源,再从那个数组取一个数,放入大小为20的数组。
重复上述过程。
最后集合内放有500个最大的数。
对于其中的排序,根据排序的时间复杂度,归并和堆排序都不错。
我自己手算试了一下,确实堆排序要更快一些。有时堆只有一步就可把最大值算出,归并要2步。
个人觉得这里用堆还是归并都不是重点了。
另外,需要记一下各类排序的时间复杂度和空间复杂度:
来自:http://en.wikipedia.org/wiki/Sorting_algorithm
上述标准答案的时间复杂度就是:519*log(2)20。我认为就是519乘以log以2为底的20.
因为,在最坏的情况下,每次要移动log(2)20个元素,从堆的最下面弄到最上面。一共要移动519次。当集合中有499个元素的时候,大小为20的数组必然要有20个,才能选出最后一个,所以一共进行了519次。
再看看我的2个算法的时间复杂度:
第一种:其实是看排序,我对一个n=10000的数组进行了排序,最好也是10000*log(2)10000,将20个数组的值赋值给10000的数组,我都不加了。
第二种:我对大小规模为1000的数组进行了排序10次,也就是10*1000*log(2)1000。比1还有点好一点
不过都没把握到解体的本质。
下面是我完成的源代码,标准答案主要在思路,我就没有写源码了。两种排序的输出结果一样的。
#include <stdlib.h>
#include <stdio.h>
#include <algorithm>
int i,j,x;
int A[20][500];
int MAX[500];
/*解法一,把这个20个数组放到一个数组内,再一降序排序,前500个就是最大的。
这是个很没有水平的办法,要是让我找出10000000000000000000000个数的前5个,难道我还要遍历?但貌似不遍历也没有办法啊
*/
void solutionOne(){
int a[10000];
x=0;
for(i=0;i<20;i++){
for(j=0;j<500;j++){
a[x]=A[i][j];
x++;
}
}
std::sort(a,a+10000);
for(j=9999;j>9499;j--){
printf("-%d-",a[j]);
}
}
/*这个是qsort函数需要函数,用来区别升序还是降序,我们这里是降序*/
int comp ( const void *a, const void *b )
{
return *(int *)b-*(int *)a;//这样表示降序排列
}
void findMax500(int m){
int temp[1000];
x=0;
for(j=0;j<500;j++){
temp[x]=MAX[j];
x++;
}
for(j=0;j<500;j++){
temp[x]=A[m][j];
x++;
}
qsort(temp,1000,sizeof(int),comp);
for(j=0;j<500;j++){
MAX[j]=temp[j];
}
}
void main(){
//创建数组并赋值
for(i=0;i<20;i++){
for(j=0;j<500;j++){
A[i][j]=rand()%100000+1;
}
}
//对每个数组排序,达到题目要求
for(i=0;i<20;i++){
std::sort(A[i],A[i]+500);
}
//打印其中某一个,看看是不是已经排好序了
/*for(j=0;j<500;j++){
printf("%d-",A[5][j]);
}*/
/*现在已经完成了题设的要求*/
/*解法二:写一个方法,将两个数组,将两个数组整合到一个数组里面,排序,前500个就是我们需要的,
再反复调用这个方法。有一个MAX先装满0,然后它先与A[1]装在一个1000的数组内后,排序,把前500个再复制给Max,
然后再让MAX与A[2]重复上述过程,一直到A[19];
*/
for(j=0;j<500;j++){//初始化数组MAX,使其全为0
MAX[j]=0;
}
for(i=0;i<20;i++){
findMax500(i);
}
for(j=0;j<500;j++){
printf("+%d+",MAX[j]);//打印出来后与solutionOne对比
}
solutionOne();
system("pause");
}