统计单词出现频率及排序 从单机到多机合作 图文示例

 

本文是学习 多线程服务端编程的练习

书籍作者陈硕的博客也有提到这个题目

http://blog.csdn.net/solstice/article/details/8497475

 

第一个层次很简单 单机 一个小文件 读进来进行处理 然后对每个单词进行统计排序 记录每个单词出现频率

  

第二个层次 就是文件较大 单词量较多 如果一次性读入并使用vector容器存放 以及使用hash容器进行统计频率 会出现内存等资源不足现象

那么就依次读取进内存 将解析的单词存放进vector中,当vector中的容量到达一个阈值 将vector中的单词放进map容器中统计单词出现频率 vector容器清空

但是map容器有多个 各个单词存放进那个map容器根据hash函数决定  

当map容器的容量达到阈值后 写入文件 map容器清空

这样我们就得到多个记录单词出现频率的文本 供后继步骤分析合并 但是一个单词的出现频率肯定只出现在一个记录文本中(因为相同的单词具有相同的hash值 对应同一个记录文本)

如图

 

代码如下(记录合并代码后继完成)

  运行效果如图

运行前

运行后

 

合并代码如下

  合并过程如图

 

 

代码如下

 

复制代码
// WordFrequentMerge.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <algorithm>
#include <iostream>
#include <unordered_map>
#include <deque>
#include <vector>
#include <sstream>
#include <fstream>

using namespace std;


#define DEFAULT_FILE_NAME    "0bucket.txt"
#define BUCKET_NUM    10
std::ifstream in[BUCKET_NUM];
std::deque<std::pair<int, std::string>> deqFreqRecord[BUCKET_NUM];



void OpenFile() {
    std::string fileName = "0bucket.txt";
    for (int i = 0; i < BUCKET_NUM; i++) {
        in[i].close();
        in[i].open(fileName);
        if (in[i].bad() || !in[i]) {
            std::cerr << "open file error.exit!!" << std::endl;
            return;
        }
        fileName[0]++;
    }
}

void ReadSortEle_(std::ifstream& in, std::deque<std::pair<int, std::string>>& d) {
    std::string line,word;
    int freq;
    try {
        while (getline(in, line)) {
            std::string::size_type pos = line.find('\t');
            if (pos != std::string::npos) {
                freq = std::stoi(line.substr(0, pos));
                word = line.substr(pos + 1);
                //std::cout << word << "\t" << freq << std::endl;
                d.push_back(std::make_pair(freq, word));
            }
        }
    }
    catch (std::exception& e) {
        std::cout << e.what() << std::endl;
        return;
    }

    std::sort(d.begin(),d.end(), [](const std::pair<int, std::string>& lhs,  
        const std::pair<int, std::string>& rhs) {
        return lhs.first > rhs.first;
    });
}


void ReadSortEle(std::ifstream in[], std::deque<std::pair<int, std::string>> d[]) {
    for (int i = 0; i < BUCKET_NUM; i++) {
        ReadSortEle_(in[i],d[i]);
    }
}

bool HeapCompareFunc(std::pair<int, std::string>& l,
    std::pair<int, std::string>& r) {
    return l.first < r.first;
}

void MergeFrequent(std::deque<std::pair<int, std::string>> d[]) {
    vector<std::pair<int, std::string>> vecHeap;
    for (int i = 0; i < BUCKET_NUM; i++) {
        if (!d[i].empty()) {
            vecHeap.push_back(d[i].front());
            d[i].pop_front();
        }
    }

    std::make_heap(vecHeap.begin(), vecHeap.end(), HeapCompareFunc);

    while (!vecHeap.empty()) {
        std::pop_heap(vecHeap.begin(), vecHeap.end(), HeapCompareFunc);
        std::pair<int, std::string> p = vecHeap.back();
        vecHeap.pop_back();

        std::cout << p.second << "\t" << p.first << std::endl;

        int idx = std::hash<string>()(p.second) % BUCKET_NUM;
        
        if (!d[idx].empty()) {
            vecHeap.push_back(d[idx].front());
            std::push_heap(vecHeap.begin(), vecHeap.end(), HeapCompareFunc);
            d[idx].pop_front();
        }
    }

}


int main()
{
    OpenFile();
    ReadSortEle(in, deqFreqRecord);
    MergeFrequent(deqFreqRecord);

    return 0;
}
View Code
复制代码

 

 

 

 

代码还有诸多待优化地方

比如读入单词后 另开线程处理而不是等待处理完毕后再读取单词

比如数据的传递 考虑传递指针和起始位置 而不是传递拷贝

posted on   itdef  阅读(492)  评论(1编辑  收藏  举报

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
历史上的今天:
2015-02-20 监控系统 内存占用率并记录于本地文件中

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示