题目描述:
输入:一亿个整数,有重复的数字,整数保存在一个文件中
输出:文件中最大的k个数
限制:尽量以最快的速度完成任务。
具体解决方法:
1. 位图解决
位图为用比特位来存储数据,如果i比特位为1,则该位在表示整数i,为0,则不是
用该方法主要提供三个函数接口:设置比特位:set_bit(int *data, int num)
清除比特位:clr_bit(int *data, int num)
获得某个比特位:get_bit(int *data, int num)
这些函数具体实现都用位操作实现
具体实现代码:
#include <iostream> #include <cstdio> #include <sys/time.h> #include <cstdlib> using namespace std; #define BITSIZE 32 #define SHIFT 5 #define SIZE 10000000 void set_bit(int *data, int num) { data[num>>SHIFT] |= 0x01<<(num&(BITSIZE-1)); } void clr_bit(int *data, int num) { data[num>>SHIFT] &= ~(0x01<<(num&(BITSIZE-1))); } int get_bit(int *data, int num) { return (data[num>>SHIFT] & (0x01<<(num&(BITSIZE-1)))); } int main(void) { FILE *fp; int k=0; fp=fopen("10_million_data.txt", "r"); struct timeval start, end; int a[312500]={0}; int num=0; gettimeofday(&start, NULL); while(!feof(fp)) { fscanf(fp, "%d", &num); if(feof(fp)) break; set_bit(a, num); } for(long long int i=SIZE; i>=1; i--) { if(get_bit(a, i)) { ++k; if(k>10) break; cout << i << " "; } } cout << endl; gettimeofday(&end, NULL); float t_time = end.tv_sec-start.tv_sec+(end.tv_usec-start.tv_usec)/1000000.0; cout << "time used : " << t_time << endl; return 0; }
2. 用标准库关联性容器set实现
说明:set中的元素是有序的,默认是升序的,且数组元素不重复
边插入边排序,具体代码如下:
int main(void) { set<int> s; FILE *fp; int num=0; fp=fopen("10_million_data.txt", "r"); struct timeval start, end; gettimeofday(&start, NULL); //linux系统调用函数 while(!feof(fp)) { fscanf(fp, "%d", &num); s.insert(num); } gettimeofday(&end, NULL); float t_time = end.tv_sec-start.tv_sec+(end.tv_usec-start.tv_usec)/1000000.0; cout << t_time << endl; //set<int>::iterator it=s.begin(); //for(; it!=s.end(); ++it) return 0; }
思路:先用文件中的前十个数初始化为一个10个元素的小根堆,默认为较大的10个数,再逐个从文件中读文件,比根顶元素大就调整小根堆,知道文件读完为止
具体实现代码:
void exch(long int &a, long int &b) { long int temp=a; a=b; b=temp; } void sink(long int *data, int pos) int l=2*pos; if(l<data[0] && data[l]>data[l+1]) l++; if(data[l]>data[pos]) break; exch(data[l], data[pos]); pos=l; } } void swim(long int *data, int pos) { while(pos>1 && data[pos/2]<data[pos]) { exch(data[pos/2], data[pos]); pos=pos/2; } } void select_topk(FILE *pf, long int *data) { int i=0, j=10; long int num=0; for(i=k/2; i>=1; i--) sink(data, i); while(!feof(pf)) { fscanf(pf, "%ld", &num); if(feof(pf)) break; j++; if(num>data[1]) { exch(data[1], num); sink(data, 1); } } cout << j << endl; } int main(void) { long int topk[k+1]={k}; struct timeval start, end; long int num=1; FILE *pf; //pf=fopen("3milliondata.txt", "r"); pf=fopen("10_million_data.txt", "r"); gettimeofday(&start, NULL); for(long int i=1; i<=10; i++) { fscanf(pf, "%ld", &topk[i]); } select_topk(pf, topk); gettimeofday(&end, NULL); float time=end.tv_sec-start.tv_sec+(end.tv_usec-start.tv_usec)/1000000.0; printf("time is used %f\n", time); for(int i=1; i<=k; i++) cout << topk[i] << " "; cout << endl; return 0; }
另外在小数据时寻找mink或者maxk,可以考虑使用快速排序的思想来实现,具体代码如下
void exch(int &a, int &b) { int temp=a; a=b; b=temp; } int Less(int a, int b) { return (a<b ? 1 : 0); } int randint(int min, int max) { return (rand()%(max-min+1)+min); } void qsort_mink(int *data, int l, int h, int k) { int i=l, j=h+1; exch(data[l], data[randint(l+1, h)]); int key = data[l]; while(1) { while(Less(data[++i], key)) if(i==h) break; while(!Less(data[--j], key)) if(j==l) break; if(i>j) break; exch(data[i], data[j]); } exch(data[l], data[j]); if(j>k) qsort_mink(data, l, j-1, k); //在j的左半边寻找 if(j<k) qsort_mink(data, j+1, h, k); //在j的右半边寻找 }该方法是时间复杂度为O(nlogn)
总结:
从运行结果中看出:小根堆排序实现 < 位图实现 < C++STL实现
所以topk问题一般用小根堆或大根堆实现,或者可以使用位图(当然数字一大时就会出现内存不够的现象)