编程练习(三)(Top-K选择)

Top-K选择

  • 说明
    random文件里面含有一系列正整数。要求选择其中最大的k(k=10)个数,并显示。

  • 目的
    考察top-K选择算法的效率,本次将k设置为10。

  • 程序流程
    1、读文件到内存数组A;
    2、记录程序开始时间;
    3、循环1百次
    4、 执行top-K选择算法;
    (要求不能改变数组A中的内容,对数组A只能读,不能写)
    5、循环1百次结束
    6、记录程序结束时间;
    7、显示1百万次循环的执行时间;
    8、显示选择的top-K个数。
    9、程序编完之后,理论上分析一下算法的时间复杂性。


解决方法

(解决方法的实现代码可能还存在着问题,但思路是正确的。)

  • 第一种
    对n个数由大到小排序,然后选择前k个最大的数。

  • 排序算法有很多种,快速排序应该综合看来是最优,本代码使用最简单但也是最慢的排序算法来解决,时间复杂度为o(k*n^2)。

  • c语言实现如下

#include
#include

int Read();
int i=0,a[100000];

int main(){
	clock_t start,end;
	start=clock();
	float runtime;
	int j=0,k=0;
	Read();
	for(int n=0;n<10000;n++){
		while(a[j]){
			k=j;
			while(a[k]){
				if(a[j]<=a[k]){
					int x=0;
					x=a[j];
					a[j]=a[k];
					a[k]=x;
				}
				k++;
			}
			k=0;
			j++;
		}
		printf("第%d次Top-10=",n+1);
		for(j=0;j<10;j++){
			printf("%d ",a[j]);
		}
		printf("\n");
		j=0;
	}
	end=clock();
	runtime=(double)(end-start);
	printf("程序运行时间为%f毫秒(ms)\n",runtime);
	return 0;
}

int Read(){
	FILE *data;
	data=fopen("random.txt","r");
	while(fscanf(data,"%d",&a[i])!=EOF){
		i++;
	}
	fclose(data);
}
  • 第二种
    (1)扫描数组A, 选择最大的数,并做标记;
    (2) 扫描数组A, 选择第二大的数,并做标记;
    ……
    (k) 扫描数组A, 选择第K大的数,并做标记;

  • 代码仍存在问题未能解决,在于每次将数组中的最大值置1后,无法恢复,如果每次使用Read()函数重读只能运行十次,整体来看时间复杂度为o(kn)。

  • c语言实现如下

#include
#include
#include

int Read();
int i=0,a[100000];

int main(){
	clock_t start,end;
	start=clock();
	float runtime;
	int j=0,num=0,max=0,k=0,b[10]={0};
	Read();
	for(int n=0;n<10;n++){
		for(j=0;j<10;j++){
		    max=0,k=0;
			while(a[k]){
				if(a[k]>max){
					max=a[k];
					num=k;
				}
				k++;
			}
			b[j]=max;
			a[num]=1;
		}
		printf("第%d次Top-10=",n+1);
		for(j=0;j<10;j++){
			printf("%d ",b[j]);
		}
		printf("\n");
	}
	end=clock();
	runtime=(double)(end-start);
	printf("程序运行时间为%f毫秒(ms)\n",runtime);
	return 0;
}

int Read(){
	FILE *data;
	data=fopen("random.txt","r");
	while(fscanf(data,"%d",&a[i])!=EOF){
		i++;
	}
	fclose(data);
}
  • 第三种
    把数组的前k个数先放入优先级队列(底层实现是小根堆,STL模板库有现成的库函数), 从第k+1个数开始扫描。
    (1) 如果当前数比优先级队列的队列头(小根堆的堆顶)大,说明当前数需要放入优先级队列,这时先弹出优先级队列的队列头,然后将当前数插入优先级队列。
    (2) 如果当前数比优先级队列的队列头(小根堆的堆顶)小,说明当前数不会是top-k中的数,直接滤过,处理下一个数。

  • 在放入优先级队列比较时,小根堆是最优的比较方法,时间复杂度仅为堆的高度h=log(2)k(即以2为底k的对数),总体时间复杂度再乘以外界的n,即o(n*log(2)k)。
    本代码使用队列内逐个比较方法,所以时间复杂度为o(kn)。

  • c语言实现如下

#include
#include
#include

int Read();
int i=0,a[1000000];

int main(){
	clock_t start,end;
	start=clock();
	float runtime;
	int h[10]={0};
	int k=0,x=0,j=0;
	Read();
	for(int n=0;n<1000000;n++){
		j=0;
		while(a[j]){
			if(a[j]>h[0]){
				h[0]=a[j];
				for(k=0;k<9;k++){
					if(h[k]>h[k+1]){
						x=h[k];
						h[k]=h[k+1];
						h[k+1]=x;
					}
				}
			}
			j++;
		}
		printf("第%d次Top-10=",n+1);
		for(k=9;k>=0;k--){
			printf("%d ",h[k]);
			h[k]=0;
		}
		printf("\n");
	}
	end=clock();
	runtime=(double)(end-start);
	printf("程序运行时间为%f毫秒(ms)\n",runtime);
	return 0;
}

int Read(){
	FILE *data;
	data=fopen("random.txt","r");
	while(fscanf(data,"%d",&a[i])!=EOF){
		i++;
	}
	fclose(data);
}

备注

  • 打印数据需要花费很长时间,如果只打算比较各算法的运行时间,可以将打印的部分注释掉。
  • 计算运行时间需要调用time.h,并有计算时长代码如下
#include
clock_t start,end;
start=clock();
float runtime;
end=clock();
runtime=(double)(end-start);
printf("程序运行时间为%f毫秒(ms)\n",runtime);
  • 打开一个文件,并读取其中数据的函数实现如下
int i=0,a[1000000];
int Read(){
	FILE *data;
	data=fopen("random.txt","r");
	while(fscanf(data,"%d",&a[i])!=EOF){
		i++;
	}
	fclose(data);
}
  • random.txt文件中包含的是诸如下列形式的许多整数,整数大小与数量无要求,可以自行生成。
    12960
    560
    28628
    9692
    2842
    30927

你可能感兴趣的:(编程练习)