来学校交流学习的第一个正式的小项目作业就是软件工程老师所提出的词频统计了,具体要求如下。
要求:
写一个程序,分析一个文本文件中各个词出现的频率,并且把频率最高的10个词打印出来。文本文件大约是30KB~300KB大小。
解决思路:
刚看到这个问题,我脑海浮现的问题就是如何存储如此大量的数据呢,然后如何进行有效的统计。
我在想解决方案时,也有参考以前学姐学长们的例子,发现大多数好像都是用数组或者是链表来实现。
虽然老师所要求的文章大小并不算大,但是,推而广之考虑到文件大小更大的文章呢。所以单词量不是像原来所读取的那样少,若简单用数组链表实现的话,每匹配一个词就要把所有的单词遍历一遍显然是效率不高的,且读取以后要遍历多次进行单词的匹配,以便统计相同单词的个数。所以就考虑到一个效率的问题,恰好最近看到有关海量数据处理的相关文章,所以这里我首先就想到用字典树来存储数据。
字典树我就不详细介绍了,前一篇博文有详细介绍。Trie的核心思想是空间换时间,利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。Trie的典型应用是统计和排序大量的字符串,所以经常被搜索引擎系统用于文本词频统计。
(以上仅是个人见解,有任何异议错漏欢迎提出,谢谢。)
整个开发时间历时两天左右,一天时间来学习字典树相关知识,另一天来实现该程序代码。
实现代码:
1.字典树的相关定义:
(dic.h)
using namespace std ;
class DicNode
{
public:
DicNode() ;
virtual ~DicNode() ;
public:
int count ;
bool word ;
DicNode* next[26] ;
} ;
class Dic : public AddWord
{
public:
Dic() ;
virtual ~Dic() ;
private:
DicNode* head ;
public:
bool addFilter(Filter* filter) ;
bool addWord (string word) ;
bool travel(WordSord* pool) ;
private:
void travel_inside(DicNode* p, string data, WordSord* pool) ;
vector filter_array ;
} ;
#include "dic.h"
DicNode::DicNode()
{
this->count = 0 ;
this->word = false ;
for (int i = 0 ; i < 26 ; ++i)
{
this->next[i] = NULL ;
}
}
DicNode::~DicNode()
{
for (int i = 0 ; i < 26 ; ++i)
{
if (this->next[i])
{
delete next[i] ;
this->next[i] = NULL ;
}
}
}
Dic::Dic()
{
this->head = new DicNode() ;
}
Dic::~Dic()
{
if (this->head)
{
delete this->head ;
this->head = NULL ;
}
}
bool Dic::addFilter(Filter* filter)
{
if (NULL == filter)
{
return false ;
}
this->filter_array.push_back(filter) ;
return true ;
}
bool Dic::addWord(std::string word)
{
DicNode* p = this->head ;
for (int i = 0 ; i < (int)word.length() ; ++i)
{
// ≈–∂œ «≤ª «“™±ª»•≥˝µÙ
for (int j = 0 ; j filter_array.size(); ++j)
{
if (this->filter_array[j]->existWord(word))
{
return true ;
}
}
// ◊™≥…–°–¥
if (word[i] >= 'A' && word[i]<= 'Z')
{
word[i] = word[i] - 'A' + 'a' ;
}
int index = word[i] - 'a' ;
if (NULL == p->next[index])
{
p->next[index] = new DicNode() ;
}
p = p->next[index] ;
if (i == word.length()-1)
{
p->word = true ;
p->count++ ;
}
}
return true ;
}
bool Dic::travel(WordSord* pool)
{
if (NULL == pool)
{
return false ;
}
string data ;
this->travel_inside(this->head, data, pool) ;
return true ;
}
void Dic::travel_inside(DicNode* p, string data, WordSord* pool)
{
if (NULL == p)
{
return ;
}
if (p->word)
{
//cout <count<< endl ;
pool->addString(data, p->count) ;
}
for (int i = 0 ; i < 26 ; ++i)
{
if (p->next[i])
{
string temp = data + char('a' + i) ;
travel_inside(p->next[i], temp, pool) ;
}
}
}
2.读取文件内容
关键代码:
#include "file.h"
#include
using namespace std ;
bool File::readFromFile(string file, AddWord* object)
{
if (NULL == object)
{
return false ;
}
string lines ;
ifstream in(file.c_str()) ;
if (in)
{
string word = "" ;
while (getline (in, lines))
{
for (int i = 0 ; i < (int)lines.length() ; ++i)
{
if ((lines[i] >= 'a' && lines[i] <= 'z')
|| (lines[i] >= 'A' && lines[i] <= 'Z'))
{
word += lines[i] ;
}
else
{
if (word != "")
{
object->addWord(word) ;
}
word = "" ;
}
}
if (word != "")
{
object->addWord(word) ;
word = "" ;
}
}
if (word != "")
{
object->addWord(word) ;
word = "" ;
}
}
else
{
cout<< "open file" << file << " failed..." << endl ;
}
return true ;
}
关键代码:
#include "word_sord.h"
using namespace std;
bool operator < (const word_node& l, const word_node& r)
{
if (l.count <= r.count)
{
return false ;
}
return true ;
}
WordSord::WordSord(int _size)
{
this->_size = _size ;
}
bool WordSord::addString(string word, int num)
{
if (this->_size <= 0)
{
return true ;
}
word_node _node ;
_node.count = num ;
_node.word = word ;
if ((int)this->array.size() < _size)
{
this->array.push_back(_node) ;
}
else
{
int min_index = 0 ;
int min_data = array[0].count ;
for (int i = 0 ; i < (int)array.size() ; ++i)
{
if (min_data > array[i].count)
{
min_data = array[i].count ;
min_index = i ;
}
}
if (num > min_data)
{
array[min_index] = _node ;
}
}
return true ;
}
bool WordSord::print_r()
{
sort(array.begin(), array.end()) ;
for (int i = 0 ; i < (int)this->array.size() ; ++i)
{
cout<
//
// main.cpp
// dictree
//
// Created by Emily on 14-10-6.
// Copyright (c) 2014年 Emily. All rights reserved.
//
#include
#include "time.h"
#include "file.h"
#include "dic.h"
#include "word_sord.h"
#include
using namespace std ;
int main()
{
// ¥¥Ω®◊÷µ‰ ˜
Dic dic ;
clock_t start_time = clock();
// ∂¡»°Œƒº˛
File file ;
file.readFromFile("/Users/emily/Documents/dictree/dictree/1.txt" , &dic) ;
// ¥¥Ω®≈≈–Ú
WordSord sord(10) ;
// ±È¿˙◊÷µ‰ ˜
dic.travel(&sord) ;
// ¥Ú”°≥ˆ¿¥ ˝æ›
sord.print_r() ;
system("pause") ;
clock_t end_time = clock();
cout<<"Running Time is:"<(end_time-start_time)/CLOCKS_PER_SEC*1000<<"ms"<
性能分析1(780KB左右):
测试文档大小:
测试结果:
相关性能:
性能分析2(50MB左右):
测试文档大小
测试结果:
相关性能: