題目描述:
輸入:一億個整數,有重複的數字,整數儲存在一個檔案中
輸出:檔案中最大的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 10000000void 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;}
3. 用10個小根堆實現尋找較大的10個數,時間複雜度為O(10000000*logk)
思路:先用檔案中的前十個數初始化為一個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問題一般用小根堆或大根堆實現,或者可以使用位元影像(當然數字一大時就會出現記憶體不夠的現象)