题目:有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词.
首先,我们看到这个题目应该做一下计算,大概的计算,因为大家都清楚的知道1G的文件不可能用1M的内存空间处理。所以我们要按照1M的上线来计算,假设每个单词都为16个字节,那么1M的内存可以处理多少个单词呢? 1M = 1024 KB = 1024 * 1024 B 。然后1M / 16B = 2^16个单词,那么1G大概有多少个单词呢? 有2^26个单词,但是实际中远远不止这些,因为我们是按照最大单词长度算的。我们需要把这1G的单词分批处理,根据上面的计算,可以分成大于2^10个文件。索性就分成2000个文件吧,怎么分呢,不能随便分,不能简单的按照单词的顺序然后模2000划分,因为这样有可能相同的单词被划分到不同的文件中去了。这样在统计个数的时候被当成的不同的单词,因为我们没有能力把在不同文件中相同单词出现的次数跨越文件的相加,这就迫使我们要把不同序号的同一个单词划分到同一个文件中:应用hash统计吧。稍后代码会给出方法。然后呢,我们队每个文件进行分别处理。按照key-value的方法处理每个单词,最终得出每个文件中包含每个单词和单词出现的次数。然后再建立大小为100的小根堆。一次遍历文件进行处理。我没有弄1G的文件,弄1M的,简单的实现了一下,不过原理就是这样的。这是单词:http://download.csdn.net/detail/zzran/4934173
- #include<iostream>
- #include<string>
- using namespace std;
- #define FILE_NUM 10
- #define WORDLEN 30
- #define HASHLEN 7303
- typedef struct node_no_space{
- char *word;
- int count;
- struct node_no_space *next;
- }node_no_space, *p_node_no_space;
- typedef struct node_has_space{
- char word[WORDLEN];
- int count;
- struct node_has_space *next;
- }node_has_space, *p_node_has_space;
- p_node_no_space bin[HASHLEN] = {NULL};
- void swap(int *a, int *b) {
- int temp;
- temp = *a;
- *a = *b;
- *b = temp;
- }
- unsigned int hash(char *p_word) {
- unsigned int index = 0;
- while(*p_word) {
- index += index * 31 + *p_word;
- p_word++;
- }
- return index % HASHLEN;
- }
- int trim_word(char *word) {
- int n = strlen(word) - 1;
- int i = 0;
- if(n < 0)
- return 0;
- while(word[n] < '0' || (word[n] > '9' && word[n] < 'A') || (word[n] > 'Z' && word[n] < 'a') || word[n] > 'z') {
- word[n] = '\0';
- n--;
- }
- if(n < 0)
- return 0;
- while(word[i] < '0' || (word[i] > '9' && word[i] < 'A') || (word[i] > 'Z' && word[i] < 'a') || word[i] > 'z') {
- i++;
- }
- strcpy(word, word + i);
- return 1;
- }
- void insert_word(char *p_word) {
- unsigned int index = hash(p_word);
- node_no_space *p;
- for(p = bin[index]; p != NULL; p = p->next) {
- if(strcmp(p_word, p->word) == 0) {
- (p->count)++;
- return;
- }
- }
- p = (node_no_space*)malloc(sizeof(node_no_space));
- p->count = 1;
- p->word = (char*)malloc(strlen(p_word) + 1);
- strcpy(p->word, p_word);
- p->next = bin[index];
- bin[index] = p;
- }
- void min_heap(node_has_space *heap, int i, int len) {
- int left = 2 * i;
- int right = 2 * i + 1;
- int min_index = 0;
- 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[min_index].count, &heap[i].count);
- char buffer[WORDLEN];
- strcpy(buffer, heap[min_index].word);
- strcpy(heap[min_index].word, heap[i].word);
- strcpy(heap[i].word, buffer);
- min_heap(heap, min_index, len);
- }
- }
- void build_min_heap(node_has_space *heap, int len) {
- int index = len / 2;
- int i;
- for(i = index; i >= 1; i--) {
- min_heap(heap, i, len);
- }
- }
- void destroy_bin() {
- node_no_space *p, *q;
- int i = 0;
- while(i < HASHLEN) {
- p = bin[i];
- while(p) {
- q = p->next;
- if(p->word) {
- free(p->word);
- p->word = NULL;
- }
- free(p);
- p = NULL;
- p = q;
- }
- bin[i] = NULL;
- i++;
- }
- }
- void write_to_file(char *path) {
- FILE *out;
- if((out = fopen(path, "w")) == NULL) {
- cout << "error, open " << path << " failed!" << endl;
- return;
- }
- int i;
- node_no_space *p;
- i = 0;
- while(i < HASHLEN) {
- for(p = bin[i]; p != NULL; p = p->next) {
- fprintf(out, "%s %d\n", p->word, p->count);
- }
- i++;
- }
- fclose(out);
- destroy_bin();
- }
- void main() {
- char word[WORDLEN];
- char path[20];
- int count;
- int n = 10;
- unsigned int index = 0;
- int i;
- FILE *fin[10];
- FILE *fout;
- FILE *f_message;
- node_has_space *heap = (node_has_space*)malloc(sizeof(node_has_space) * (n + 1));
- // divide word into n files
- if((f_message = fopen("words.txt", "r")) == NULL) {
- cout << "error, open source file failed!" << endl;
- return;
- }
- for(i = 0; i < n; i++) {
- sprintf(path, "tmp%d.txt", i);
- fin[i] = fopen(path, "w");
- }
- while(fscanf(f_message, "%s", word) != EOF) {
- if(trim_word(word)) {
- index = hash(word) % n;
- fprintf(fin[index], "%s\n", word);
- }
- }
- for(i = 0; i < n; i++) {
- fclose(fin[i]);
- }
- // do hash count
- for(i = 0; i < n; i++) {
- sprintf(path, "tmp%d.txt", i);
- fin[i] = fopen(path, "r");
- while(fscanf(fin[i], "%s", word) != EOF) {
- insert_word(word);
- }
- fclose(fin[i]);
- write_to_file(path);
- }
- // heap find
- for(i = 1; i <= n; i++) {
- strcpy(heap[i].word, "");
- heap[i].count = 0;
- }
- build_min_heap(heap, n);
- for(i = 0; i < n; i++) {
- sprintf(path, "tmp%d.txt", i);
- fin[i] = fopen(path, "r");
- while(fscanf(fin[i], "%s %d", word, &count) != EOF) {
- if(count > heap[1].count) {
- heap[1].count = count;
- strcpy(heap[1].word, word);
- min_heap(heap, 1, n);
- }
- }
- fclose(fin[i]);
- }
- for(i = 1; i <= n; i++)
- cout << heap[i].word << ":" << heap[i].count << endl;
- }
运行结果,虽然与用文本文件查找的有差别,但是还是差不了多少的,因为文件中有不规范的单词。