• C++回顾 统计词频问题 -- vector、map、hash_map(三种方式时间比较)


    本博文我们通过三个程序比较统计词频问题的时间复杂度问题(末尾有用时及其分析);

    问题描述;

    1)、找一篇文章,将所有单词输入至程序;(The Bible Holy为例)

    2)、统计出每个单词的数量,即词频问题;

    3)、增加停用词功能;(遇到此类词,直接略过)(网上搜)

    4)、分别统计出读取文件并计算词频时间、排序所用时间;

    5)、用 类 实现各函数(处统计时间的函数除外)。

     vector、map、hash_map 都要处理字符串的 去除标点符号、将大写字母转换成小写字母、不对数字进行统计 问题。因此,我们可以将处理这些函数进行封装。

    StringUtil.h

     1 #ifndef STRING_UTIL_H_
     2 #define STRING_UTIL_H_
     3 
     4 #include <string>
     5 
     6 namespace stringutil
     7 {
     8 void erasePunct(std::string &s);
     9 void stringToLower(std::string &s);
    10 bool isAllDigit(const std::string &s);
    11 }
    12 
    13 #endif  /* STRING_UTIL_H_ */

    StringUtil.cpp

     1 #include "StringUtil.h"
     2 #include <ctype.h>
     3 
     4 using namespace std;
     5 
     6 namespace stringutil
     7 {
     8 
     9 void erasePunct(string &s)
    10 {
    11     string::iterator it = s.begin();
    12     while(it != s.end())
    13     {
    14         if(ispunct(*it))
    15             it = s.erase(it);
    16         else
    17             ++it;
    18     }
    19 }
    20 void stringToLower(string &s)
    21 {
    22     for(string::iterator it = s.begin();
    23         it != s.end();
    24         ++it)
    25     {
    26         if(isupper(*it))
    27             *it = tolower(*it);
    28     }
    29 }
    30 
    31 bool isAllDigit(const std::string &s)
    32 {
    33     for(string::const_iterator it = s.begin();
    34         it != s.end();
    35         ++it)
    36     {
    37         if(!isdigit(*it))
    38             return false;
    39     }
    40 
    41     return true;
    42 }
    43 
    44 }

     一、采用vector、pair;

    采用的数据结构: 

    1)、vector<string> stopList用于存放停用词;

    2)、vector<pair<string, int> >  words  用于存放 除禁用词之外的其他The Bible Holy 的单词;

    思路:

    1)、用ReadStopFile函数 读取停用词到 stopList中;

    2)、用ReadWordFile函数读取The Bible Holy到 words中;在我们读取的过程中首先要 去除标点符号、将word转化为小写、 跳过数字

    3)、然后判断该 单词 是否在 stoplist 中,若不在,最后添加单词 至 words中。

    4)、对词频排序,这里我们采用algorithm库中的sort函数;

    5)、对words vector进行遍历,打印之;

    这里注意一点:除了在 main.cpp中显式调用的函数,我们将其他函数声明为 类 的私有函数

    函数声明如下:

     1 #ifndef WORDFREQUENCY_H_
     2 #define WORDFREQUENCY_H_
     3 
     4 #include <vector>
     5 #include <string>
     6 #include <utility>
     7 
     8 class WordFrequency
     9 {
    10     public:
    11         WordFrequency(const std::string &filename, const std::string &stopFile);//初始化
    12         void ReadStopFile();
    13         void ReadWordFile();
    14         void sortWordByFrequency();
    15         void printWordFrequency()const;
    16     private:
    17         void addWordToDict(const std::string &word);
    18         bool isStopWord(const std::string &word)const;
    19 
    20         typedef std::vector<std::pair<std::string, int> >::iterator Wordit;//为了方便
    21         typedef std::vector<std::pair<std::string, int> >::const_iterator Wordkit;
    22 
    23         std::string filename_;
    24         std::string stopFile_;
    25         
    26         std::vector<std::string> stopList_;
    27         std::vector<std::pair<std::string, int> > words_;
    28 
    29 };
    30 
    31 #endif

    函数实现如下:

     1 //WordFrequency.cpp
     2 #include "WordFrequency.h"
     3 #include "StringUtil.h"
     4 #include <algorithm>
     5 #include <stdexcept>
     6 #include <stdlib.h>
     7 #include <fstream>
     8 
     9 using namespace std;
    10 using namespace stringutil;
    11 
    12 WordFrequency::WordFrequency(const std::string &filename, const std::string &stopFile)
    13     :filename_(filename),stopFile_(stopFile)
    14 { }
    15 
    16 void WordFrequency::ReadStopFile()
    17 {
    18     ifstream in(stopFile_.c_str());
    19     if( !in )
    20         throw runtime_error("open file failure");
    21     string word;
    22     while(in >> word)
    23     {
    24         stopList_.push_back(word);
    25     }
    26     in.close();
    27 }
    28 
    29 void WordFrequency::ReadWordFile()
    30 {
    31     ifstream infile(filename_.c_str());
    32     if( !infile )
    33         throw runtime_error("open file failure!");
    34     string word;
    35     while(infile >> word)
    36     {
    37         erasePunct(word);
    38         if( isAllDigit(word))
    39             continue;
    40         stringToLower(word);
    41         if( !isStopWord(word))
    42             addWordToDict(word);
    43     }
    44     infile.close();
    45 }
    46 
    47 bool WordFrequency::isStopWord(const string &word)const
    48 {
    49     vector<string>::const_iterator it = stopList_.begin();
    50     while( it  != stopList_.end())
    51     {
    52         if( *it == word) 
    53             break;
    54         it ++;
    55     }
    56     return (it != stopList_.end());
    57 }
    58 
    59 void WordFrequency::addWordToDict(const string &word)
    60 {
    61     Wordit it = words_.begin();
    62     while( it != words_.end())
    63     {
    64         if( it->first == word)
    65         {
    66             ++ it->second ;
    67             break;
    68         }
    69         it ++;
    70     }
    71     if(it == words_.end())
    72         words_.push_back(make_pair(word, 1)) ;
    73 }
    74 
    75 bool cmp(const pair<string, int> &a, const pair<string, int>&b)
    76 {
    77     return a.second > b.second;
    78 }
    79 
    80 void WordFrequency::sortWordByFrequency()
    81 {
    82     sort(words_.begin(), words_.end(), cmp);
    83 }
    84 
    85 void WordFrequency::printWordFrequency()const
    86 {
    87     Wordkit it = words_.begin();
    88     while(it != words_.end())
    89     {
    90         printf("words: %s, frequency: %d
    ",it->first.c_str(),it->second);
    91         it ++;
    92     }
    93 }


    main.cpp

     1 //main.cpp
     2 #ifndef _STDC_FORMAT_MACROS
     3 #define _STDC_FORMAT_MACROS
     4 #endif /* _STDC_FORMAT_MACROS */
     5 #include <inttypes.h>
     6 
     7 #include "WordFrequency.h"
     8 #include <iostream>
     9 #include <vector>
    10 #include <stdexcept>
    11 #include <sys/time.h>
    12 #include <stdio.h>
    13 #include <string.h>
    14 using namespace std;
    15 
    16 int64_t gettime();
    17 
    18 int main(int argc, const char *argv[])
    19 {
    20     if(argc < 3)
    21         throw runtime_error("exe,filename, stopfile");
    22     int64_t starttime =gettime();
    23     WordFrequency wf(argv[1], argv[2]);
    24     wf.ReadStopFile();
    25     wf.ReadWordFile();
    26     
    27     int64_t readtime = gettime();
    28     wf.sortWordByFrequency();
    29 
    30     int64_t sorttime = gettime();
    31 
    32     printf("readfile time: %lld ms
    ",(readtime-starttime)/1000); 
    33     printf("sortfile time: %lld ms
    ",(sorttime-readtime)/1000);  
    34     
    35     wf.printWordFrequency();
    36     return 0;
    37 }
    38 
    39 int64_t gettime()
    40 {
    41     struct timeval tv;
    42     ::memset(&tv, 0, sizeof tv);
    43     if(::gettimeofday(&tv, NULL) == -1)
    44     {
    45         throw runtime_error("gettimeofday");
    46     }
    47     int64_t t = tv.tv_usec;
    48     t += tv.tv_sec * 1000 * 1000;
    49     return t;
    50 }

    二、map、set;

    相较于第一种,这种方式用到的数据结构有  

    1、std::set<std::string> StopList_ 用于存放停用词;
    2、std::map<std::string, int> words_ 用于存放The Bible Holy 中除了停用词之外的词;

    3、std::vector< std::pair<std::string,int> > copyWords_ 由于map不具备排序功能,故需要将其转存均vector中。

    WordFrequency.h

     1 #ifndef WORDFREQUENCY_H_
     2 #define WORDFREQUENCY_H_
     3 
     4 #include <vector>
     5 #include <map>
     6 #include <set>
     7 #include <string>
     8 class WordFrequency
     9 {
    10     public:
    11         WordFrequency(const std::string &filename, const std::string &stopfile);
    12         void ReadStopFile();
    13         void ReadWordFile();
    14 
    15         void copyWordToVector();
    16         void sortWordByFrequency();
    17         void printFrequency()const;
    18         
    19     private:
    20         std::string filename_;
    21         std::string stopfile_;
    22 
    23         std::set<std::string> StopList_;
    24         std::map<std::string, int> words_;
    25             
    26         std::vector< std::pair<std::string,int> > copyWords_;
    27 };
    28 
    29 #endif

    WordFrequency.cpp

     1 //WordFrequency.cpp
     2 #include "WordFrequency.h"
     3 #include "StringUtil.h"
     4 #include <fstream>
     5 #include <algorithm>
     6 #include <stdio.h>
     7 #include <stdexcept>
     8 using namespace std;
     9 using namespace stringutil;
    10 
    11 WordFrequency::WordFrequency(const string &filename, const string &stopfile)
    12     :filename_(filename),stopfile_(stopfile)
    13 { }
    14 
    15 void WordFrequency::ReadStopFile()
    16 {
    17     ifstream infile(stopfile_.c_str());
    18     if( !infile )
    19         throw runtime_error("open file failure");
    20     string word;
    21     while(infile >> word)
    22         StopList_.insert(word);
    23 
    24     infile.close();
    25 }
    26 
    27 void WordFrequency::ReadWordFile()
    28 {
    29     ifstream infile(filename_.c_str());
    30     if( !infile )
    31         throw runtime_error("open file failure");
    32     words_.clear();
    33     string word;
    34     while(infile >> word) //读取单词
    35     {
    36         erasePunct(word); //去除标点
    37         stringToLower(word);//转为小写
    38         if(isAllDigit(word))//去除数字
    39             continue;
    40         if( StopList_.count(word) == 0) //set中 count 标识 word这个单词是否存在
    41             words_[word]++; //如果存在,int++, 如果不存在,增添至 map中 
    42             //words_[word]= cnt(词频数)
    43     }
    44     
    45     infile.close();
    46 }
    47 
    48 void WordFrequency::copyWordToVector()
    49 {
    50     copyWords_.clear();
    51     //back_inserter(copyWords_)
    52     //front_inserter
    53     copy(words_.begin(),words_.end(),back_inserter(copyWords_));
    54 }
    55 
    56 bool cmp(const pair<string, int> &a, const pair<string, int> &b)
    57 {
    58     return a.second > b.second;
    59 }
    60     
    61 void WordFrequency::sortWordByFrequency()
    62 {
    63     sort(copyWords_.begin(), copyWords_.end(),cmp); //排序
    64 }
    65 
    66 void WordFrequency::printFrequency() const
    67 {
    68     for(vector<pair<string, int> >::const_iterator it = copyWords_.begin(); 
    69          it != copyWords_.end(); 
    70          ++it)
    71     {
    72         printf("word :%s, frequency: %d
    ", it->first.c_str(), it->second); //打印
    73     }
    74 }


    main.cpp:

    比上一程序的main函数多了一行

    1 //多出的一行copyWordToVector
    2 int64_t readtime = gettime();
    3 wf.copyWordToVector();
    4 wf.sortWordByFrequency();


    三、哈希表(c11特性):

    程序三与程序二非常相似,只是其头文件WordFrequency.h不同,因此我们只给出 .h文件;

    注意编译时 ,g++  *.cpp -std=c++0x

    WordFrequency.h

     1 //WordFrequency.h
     2 #ifndef WORDFREQUENCY_H_
     3 #define WORDFREQUENCY_H_
     4 
     5 #include <vector>
     6 #include <unordered_map>
     7 #include <unordered_set>
     8 #include <string>
     9 class WordFrequency
    10 {
    11     public:
    12         WordFrequency(const std::string &filename, const std::string &stopfile);
    13         void ReadStopFile();
    14         void ReadWordFile();
    15 
    16         void copyWordToVector();
    17         void sortWordByFrequency();
    18         void printFrequency()const;
    19         
    20     private:
    21         std::string filename_;
    22         std::string stopfile_;
    23 
    24         std::unordered_set<std::string> StopList_;
    25         std::unordered_map<std::string, int> words_;
    26             
    27         std::vector< std::pair<std::string,int> > copyWords_;
    28 };
    29 
    30 #endif

    以上三个程序(所采用的数据结构不同)性能比较:

    程序一:读取文件时间:42997ms、排序时间 7ms;

    程序二:读取文件时间:1294ms、排序时间 10ms;

    程序三:读取文件时间: 616ms、排序时间 9ms;

    分析:

    读取时间:

    程序一采用的是vector,所以每一次 从 txt 读取一个单词 都要先遍历一遍停用词的vector,然后再顺序遍历 一次 vector,以查找该单词是否已经存在,故其平均查找时间复杂度为 O(N*N);

    程序二采用的是 map与set。map的实现 是根据 平衡二叉树(具体是红黑树),其查找平均速度是n*lgN; 而且 如果不存在时,自动添加该元素至map中,词频置为1。

    程序三采用的是哈希表, unordered_map,与unordered_set ,其查找时间复杂度为 常量O(1);

    排序时间:

    程序一所用时间比程序二、三多了几毫秒,这是因为map不具备排序功能,因为要转存至vector中。而多出来的这几毫秒就是转存时间。

  • 相关阅读:
    数学之美
    作为一个程序员,你知道每天自己在做什么吗
    搭建ssm框架log4j日志
    webpack实践——DLLPlugin 和 DLLReferencePlugin的使用
    一些实用的技巧
    详解Vue 开发模式下跨域问题
    vue resource 携带cookie请求 vue cookie 跨域
    解决vue中element组件样式修改无效
    ES6 Promise 异步操作
    js 字符串操作函数
  • 原文地址:https://www.cnblogs.com/xfxu/p/3999327.html
Copyright © 2020-2023  润新知