无需提前对文件进行排序。这样做只是一堆不必要的 I/O。
更直接的方法是:
Create an empty dictionary (hash map), keyed by word. The value is the count.
for each file
while not end of file
read word
if word in dictionary
update count
else
if dictionary full
sort dictionary by word
output dictionary to temporary file
Clear dictionary
Add word to dictionary, with count 1
end
end
if dictionary not empty
sort dictionary by word
output dictionary to temporary file
您现在拥有一定数量的临时文件,每个文件均按单词排序,并且每行包含一个单词/计数对。喜欢:
aardvark,12
bozo,3
zebra,5
创建一个最小堆,用于保存 n 个最大的项目。叫它largest_items
.
做一个标准n路合并 https://stackoverflow.com/questions/5055909/algorithm-for-n-way-merge这些临时文件。当您找到每个唯一的项目时(即合并多个文件中的所有“aardvark”条目),您可以执行以下操作:
if (largest_items.count < n)
largest_items.add(word)
else if (word.count > largest_items.peek().count)
{
// the count for this word is more than the smallest count
// already on the heap. So remove the item with the
// smallest count, and add this one.
largest_items.remove_root()
largest_items.add(word)
}
复杂:
- 构建字典的时间复杂度为 O(N),其中
N
是文件中单个单词的总数。
- 对每个临时字典进行排序的时间复杂度为 O(k log k),其中“k”是字典中的单词数。
- 编写每个临时字典的时间复杂度为 O(k)
- 合并的时间复杂度为 O(M log x),其中
M
是所有临时文件的条目总数,并且x
是临时文件的数量。
- 选择项目的时间复杂度为 O(m log n),其中
m
是唯一单词的数量,并且n
是您要选择的字数。
如果您考虑最坏情况的行为(即所有单词都是唯一的),则复杂性为(n
是总字数):
- 构建字典的时间复杂度为 O(n)
- 排序并写入临时文件是
(n/m) * (m log m)
, where m
是字典的大小。
- 合并是
n log (n/m)
.
- 选择是 O(m + (k log k)),其中
k
是您要选择的单词数,m
是唯一单词的数量。因为所有单词都是唯一的,所以它们具有相同的计数,所以你只会这样做k
插入到堆中。这m
当术语占主导地位时k
远小于m
(在这些情况下通常是这种情况)。所以选择的时间复杂度为 O(m)。
当您处理大于内存的数据集时,瓶颈通常是文件 I/O。我上面概述的算法尝试最小化 I/O。在最坏的情况下(所有单词都是唯一的),每个单词将被读取两次并写入一次。但在一般情况下,每个单词被读取一次,然后每个哈希页被写入一次并读取一次。另外,您的排序是在哈希页面而不是原始单词上进行的,并且临时文件的总大小将比原始文本小得多。