大规模数据的排序问题
有这样一个问题,向高手请教:
有一亿个单词,其中有大量单词是重复的,现在需要统计其中出现的频率最高的十个单词,怎样设计算法?
我只想出了最笨的一种方法:
定义一个结构体:
struct word
{
char *p; //用来记录单词
int num; //用来记录该单词出现的次数
};
将所有的单词分几次读入内存,这里假设每次读100万个,共需要读100次,这里不妨称得到了100个“词块”,利用逐个扫描的方法统计出每个“词块”中,各个词汇出现的次数。再利用归并的方法,将100个“词块”的统计结果合并成一个完整的“词表”,这样就统计出了每个单词出现的次数。
再设计一个结构体数组word[10],将“词表”中的前10条数据根据单词出现次数的大小排序,并按降序存储到结构体数组word[10]中。然后依次读取“词表”中的其他单词,如果某个单词出现的次数比word[9].num要大,则将其插入到word[10]数组对应的位置。这样将整个词表再次扫描一次之后,最终存储在word[10]数组中的就是出现次数最多的10个单词。
现在向各位高手请教,有没有更好的算法,精确的和近似的算法都可以,谢谢。
[解决办法]
我的想法是:因为单词的量少,所以可以设计一棵Trie树,记录所有单词,然后把这一亿个单词过一遍,就可以。时间复杂度就仅仅是扫描这1亿个单词的时间。
Trie树的结构:
struct Trie
{
unsigned int count;
size_t alphabet[26];
Trie()
{
count=0;
memset(alphabet,-1,26);
}
}
vector <Trie> wordTree;
void Init()
{
wordTree.push_back(Trie());
}
//假定单词之间用1个空格间隔
void Count(char *buffer, size_t len)
{
size_t node = 0, temp;
size_t i=0;
while( i <len )
{
if( buffer[i]== ' ')
{
trieTree.at(node).count++;
node = 0; //回到根节点,重新匹配下一个单词
}
MakeLowerCase(buffer[i]);
if( (temp=trieTree.at(node).alphabet[buffer[i]- 'a '])==-1 )//新节点,增添
{
trieTree.push_back(Trie());
trieTree.at(node).alphabet[buffer[i]- 'a ']=trieTree.size()-1;
node = trieTree.size()-1;
}else
{ node = temp;
}
i++;
}
}
[解决办法]
字符串逐个比较太慢了,强烈推荐hash,或者使用C++的map试一试。
[解决办法]
楼上的, strcmp并不一定慢,你有算字符串hash的功夫其实strcmp可能已经出结果了。
可以考虑用堆来统计字符串的词频,然后再遍历堆找出10个频率最高的。
其他的我还真没什么好方法