百度笔试题-从20数组内取出最大的500个

该博客讨论了一道百度笔试题,涉及如何从20个有序数组中选取最大的500个数字。博主首先介绍了暴力方法和较优方法,后者利用结构体和最大堆,实现时间复杂度为519*log(2)20。博主还比较了堆排序和归并排序,并提供了自己的解决方案和时间复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题干

20个有序数组,每个数组有500个数字,取出这10000个数字中最大的500个,怎么做?

我的做法

(详见最后代码):

  1. 蛮力法:将这个10000个数组,放入一个数组内,然后排序,取出前500个。
  2. 写一个方法,将两个数组,将两个数组整合到一个数组里面,排序,前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");
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值