寻找最大的K个数,Top K问题的堆实现
1000萬個數據太大,打開文件很慢,可能會看不到,可以測試1萬個數據中找最大的10個。
搜索引擎熱門查詢統計
題目描述:
搜索引擎會通過日志文件把用戶每次檢索使用的所有檢索串都記錄下來,每個查詢串的長度為1-255字節。
假設目前有一千萬個記錄,這些查詢串的重復度比較高,雖然總數是1千萬,但如果除去重復后,不超過3百萬個。一個查詢串的重復度越高,說明查詢它的用戶越多,也就是越熱門。請你統計最熱門的10個查詢串,要求使用的內存不能超過1G。
解決方法:hash表+堆,去重復后不超過300萬個,總大小不超過300萬*255B=765MB,內存使用不超過1G。
第一步:先對這批海量數據預處理,在O(N)的時間內用Hash表完成去重復操作。
第二步:借助堆這個數據結構,找出Top K,時間復雜度為NlogK。即借助堆結構,可以在log量級的時間內查找和調整/移動。因此,維護一個K(該題目中是10)大小的小根堆(Kmin設為堆頂元素),然后遍歷300萬的Query,分別和Kmin進行對比比較(若X>Kmin,則更新并調整堆,否則,不更新),最終的時間復雜度是:O(N)+ N'*O(logK),(N為1000萬,N’為300萬)。
為了降低實現上的難度,假設這些記錄全部是一些英文單詞,即用戶在搜索框里敲入一個英文單詞,然后查詢搜索結果,最后,要你統計輸入單詞中頻率最大的前K個單詞。復雜問題簡單化了之后,編寫代碼實現也相對輕松多了,如下:
//copyright@yansha &&July //July、updated,2011.05.08 //題目描述: //搜索引擎會通過日志文件把用戶每次檢索使用的所有檢索串都記錄下來,每個查詢串的 //長度為1-255字節。假設目前有一千萬個記錄(這些查詢串的重復度比較高,雖然總數是1千萬,但如果 //除去重復后,不超過3百萬個。一個查詢串的重復度越高,說明查詢它的用戶越多,也就是越熱門), //請你統計最熱門的10個查詢串,要求使用的內存不能超過1G。 #include <iostream> #include <string> #include <assert.h> using namespace std; #define HASHLEN 2807303 #define WORDLEN 30 // 結點指針 typedef struct node_no_space *ptr_no_space; typedef struct node_has_space *ptr_has_space; ptr_no_space head[HASHLEN]; struct node_no_space { char *word; int count; ptr_no_space next; }; struct node_has_space { char word[WORDLEN]; int count; ptr_has_space next; }; // 最簡單hash函數 int hash_function(const char *p) { int value = 0; while (*p != '\0') { value = value * 31 + *p++; if (value > HASHLEN) value = value % HASHLEN; } return value; } // 添加單詞到hash表 void append_word(const char *str) { int index = hash_function(str); ptr_no_space p = head[index]; while (p != NULL) { if (strcmp(str, p->word) == 0) { (p->count)++; return; } p = p->next; } // 新建一個結點 ptr_no_space q = new node_no_space; q->count = 1; q->word = new char [strlen(str)+1]; strcpy(q->word, str); q->next = head[index]; head[index] = q; } // 將單詞處理結果寫入文件 void write_to_file() { FILE *fp = fopen("result.txt", "w"); assert(fp); int i = 0; while (i < HASHLEN) { for (ptr_no_space p = head[i]; p != NULL; p = p->next) fprintf(fp, "%s %d\n", p->word, p->count); i++; } fclose(fp); } // 從上往下篩選,保持小根堆 void sift_down(node_has_space heap[], int i, int len) { int min_index = -1; int left = 2 * i; int right = 2 * i + 1; if (left <= len && heap[left].count < heap[i].count) min_index = left; else min_index = i; if (right <= len && heap[right].count < heap[min_index].count) min_index = right; if (min_index != i) { // 交換結點元素 swap(heap[i].count, heap[min_index].count); char buffer[WORDLEN]; strcpy(buffer, heap[i].word); strcpy(heap[i].word, heap[min_index].word); strcpy(heap[min_index].word, buffer); sift_down(heap, min_index, len); } } // 建立小根堆 void build_min_heap(node_has_space heap[], int len) { if (heap == NULL) return; int index = len / 2; for (int i = index; i >= 1; i--) sift_down(heap, i, len); } // 去除字符串前后符號 void handle_symbol(char *str, int n) { while (str[n] < '0' || (str[n] > '9' && str[n] < 'A') || (str[n] > 'Z' && str[n] < 'a') || str[n] > 'z') { str[n] = '\0'; n--; } while (str[0] < '0' || (str[0] > '9' && str[0] < 'A') || (str[0] > 'Z' && str[0] < 'a') || str[0] > 'z') { int i = 0; while (i < n) { str[i] = str[i+1]; i++; } str[i] = '\0'; n--; } } int main() { char str[WORDLEN]; for (int i = 0; i < HASHLEN; i++) head[i] = NULL; // 將字符串用hash函數轉換成一個整數并統計出現頻率 FILE *fp_passage = fopen("string.txt", "r"); assert(fp_passage); while (fscanf(fp_passage, "%s", str) != EOF) { int n = strlen(str) - 1; if (n > 0) handle_symbol(str, n); append_word(str); } fclose(fp_passage); // 將統計結果輸入文件 write_to_file(); int n = 10; ptr_has_space heap = new node_has_space [n+1]; int c; FILE *fp_word = fopen("result.txt", "r"); assert(fp_word); for (int j = 1; j <= n; j++) { fscanf(fp_word, "%s %d", &str, &c); heap[j].count = c; strcpy(heap[j].word, str); } // 建立小根堆 build_min_heap(heap, n); // 查找出現頻率最大的10個單詞 while (fscanf(fp_word, "%s %d", &str, &c) != EOF) { if (c > heap[1].count) { heap[1].count = c; strcpy(heap[1].word, str); sift_down(heap, 1, n); } } fclose(fp_word); // 輸出出現頻率最大的單詞 for (int k = 1; k <= n; k++) cout << heap[k].count << " " << heap[k].word << endl; return 0; }參考:http://blog.csdn.net/v_JULY_v/archive/2011/05/08/6403777.aspx
作者:阿凡盧 出處:http://www.cnblogs.com/luxiaoxun/ 本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。總結
以上是生活随笔為你收集整理的寻找最大的K个数,Top K问题的堆实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 获取iPhone型号
- 下一篇: 网络***那些事