编程之法之海量数据处理:寻找Top K的热词

题目:
有100万个关键字,长度小于50个字节。用有效的算法找出Top 10的热词,要求对内存的占用不超过1 MB。

分析:
这是大家面试中都被问道的问题,可以直接使用map-reducer直接解决这个问题。如果不能使用这个架构,我们手动实现,也是采用的这个思想,只是这个用文件代替节点。
1 0 6 ∗ 50 B = 50 ∗ 1 0 6 B = 50 M B 10^6 * 50B = 50 * 10^6B = 50MB 10650B=50106B=50MB
因为要求最多1MB内存,我们需要划分成50个小文件。划分的时候我们需要保证所有相同的字符串在同一个文件中,这样我们就可以单独统计每一个小文件,最后合并结果即可。当然这个文件大家都已经理解了,这里主要是从C++的代码进行实现。

首先我们明确需要几个函数:

  1. 划分函数,将相同的字符串划分到相同小文件中, 这里我们需要有一个hash函数,将每个字符串映射成整数,再对小文件个数(50)取余。

  2. 统计每个小文件中的热词,这里需要实现一个统计函数,我们可以基于最小堆进行实现。

  3. 合并所有小文件的结果的函数,合并函数

  4. 由于我们没有数据,需要写一个随机函数,生出数据,生出数据的函数
    思维导图

  5. 随机产生数据的函数

/** 随机产生200W个长度为10的字符串,写入文件中,字符[0-5] + 'a'*/
void randCreateData(const char* outputfile, const int cnt = 1000000) {
    FILE *fp = NULL;
    fp = fopen(outputfile, "w+");
    if(fp == NULL) {
        printf("File error\n");
        return;
    }
    srand((unsigned)time(0));
    for(int i = 0; i < cnt; i++) {
        char tmp[12];
        int j;
        for(j = 0; j < 5; j++) {
            tmp[j] = char(rand()%5 + 'a');
        }
        tmp[j] = '\0';
        fprintf(fp, "%s\n", tmp);
    }
    fclose(fp);
}

  1. hash key函数
/** 得到hash(str) % MOD **/
int hash_key(const char* str) {
    int value = 0;
    while(*str != '\0') {
        value = value * 31 + *str;
        value = value % MOD;
        str ++;
    }
    return value;;
}
  1. 分割函数
/** 将200w * 5B的文件,根据key划分成50个文件,每个文件100B,大约1M **/
void partation(const char* inputfile, int filecnt = 50) {
    FILE * fps[filecnt];
    for(int i = 0; i < filecnt; i++) {
        char tmpfile[20] = "./data/tmpfile";
        char s[3];
        sprintf(s, "%d", i);
        char *file = strcat(tmpfile, s);
        // printf("%s\n", file);
        fps[i] = fopen(file, "w+");
        assert(fps[i] != NULL);
    }

    FILE *fp_read = fopen(inputfile, "r");
    assert(fp_read != NULL);
    int i = 0;
    char buffer[10];
    while(! feof(fp_read)) {
        // 这样读取包含换行符
        fgets(buffer, sizeof(buffer)-1 ,fp_read);
        // 去掉换行符号
        buffer[strlen(buffer)-1] = '\0';
        // 得到字符串对应的key,写入对应的文件
        int key = hash_key(buffer) % filecnt;
        fprintf(fps[key], "%s\n", buffer);
    }
    // close 所有文件指针
    for(int i = 0; i < filecnt; i++) {
        fclose(fps[i]);
    }
    fclose(fp_read);
}

  1. 处理一个文件的函数
/** 统计单个文件中的出现的频率 **/
int preocessedOneFile(const char *inputfile, const int topK=100) {
    map<string, int> s2cnt;
    FILE *fp_read = fopen(inputfile, "r");
    assert(fp_read != NULL);
    int i = 0;
    char buffer[10];
    while(! feof(fp_read)) {
        // 这样读取包含换行符
        fgets(buffer, sizeof(buffer)-1 ,fp_read);
        // 去掉换行符号
        buffer[strlen(buffer)-1] = '\0';
        string s = buffer;
        s2cnt[s] += 1;
    }
    // cout<<s2cnt.size()<<endl;
    for(auto it=s2cnt.begin(); it!=s2cnt.end(); it++) {
        q.push(make_pair(it->first, it->second));
        if(q.size() > topK) { // 保存最大的100个
            q.pop();
        }
    }

    /*
    int k = 0;
    while(q.size()) {
        pair<string, int> t = q.top();
        cout<<t.first<<" "<<t.second<<endl;
        q.pop();
        k ++;
        if(k > 10) break;
    }
    */
   return s2cnt.size();
}


全部源码:github 源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值