设计算法,找到书中最常用的单词

时间:2012-01-06 17:27:37

标签: python algorithm data-structures hash

面试问题:

查找书中最常用的单词。

我的想法:

使用哈希表,遍历并标记哈希表。

如果书的大小已知,如果发现任何单词使用> 50%,然后跳过以下遍历中的任何新单词并仅计算旧单词。如果图书大小未知怎么办?

是O(n)和O(n)时间和空间。

有更好的想法吗?

由于

6 个答案:

答案 0 :(得分:2)

通常Heap是数据结构,当我们必须确定最多/最少使用的东西时,它很适合。

用于这些目的的偶数Python;s Counter.nlargest是通过堆数据结构实现的。

二进制堆数据结构具有以下复杂性

CreateHeap - O(1)
FindMin - O(1)
deleteMin - O(logn)
Insert - O(logn)

我对Hash(使用Python中的默认字典)和Heap(使用python中的Collections.Counter.nlargest)进行了比较,并且Hash比Heap稍好一些。

>>> stmt1="""
import collections, random
somedata=[random.randint(1,1000) for i in xrange(1,10000)]
somehash=collections.defaultdict(int)
for d in somedata:
    somehash[d]+=1
maxkey=0
for k,v in somehash.items():
    if somehash[maxkey] > v:
        maxkey=k
"""
>>> stmt2="""
import collections,random
somedata=[random.randint(1,1000) for i in xrange(1,10000)]
collections.Counter(somedata).most_common(1)
"""
>>> t1=timeit.Timer(stmt=stmt1)
>>> t2=timeit.Timer(stmt=stmt2)
>>> print "%.2f usec/pass" % (1000000 * t2.timeit(number=10)/10)
38168.96 usec/pass
>>> print "%.2f usec/pass" % (1000000 * t1.timeit(number=10)/10)
33600.80 usec/pass

答案 1 :(得分:2)

为了确定复杂性,我认为你需要考虑两个变量,n =单词总数,m =唯一单词的数量。我想最好的案例复杂度将接近于O(n log(m))的速度和O(m)的存储,假设每次迭代n个单词中的每一个,并基于哈希表构建和搜索或其他最终包含m个元素的结构。

答案 2 :(得分:2)

这实际上是map reduce的典型例子。

维基百科页面中的示例将为您提供每个唯一单词的单词计数,但您可以轻松地在reduce步骤中添加一个步骤来跟踪当前最常见的单词(使用某种互斥量来处理并发性)问题)。

如果您拥有分布式群集的计算机或高度并行化的计算机,则运行速度比使用哈希表要快得多。

答案 3 :(得分:1)

您的优化有一个概括 - 如果图书大小已知并且您看到的任何字词都有计数>剩余的单词数+次高的计数,你当前计算得最多的单词就是答案。

答案 4 :(得分:1)

您的解决方案是正确的,快速的,并且从实际角度来看可能是最好/最简单的。

其他海报的解决方案比您的解决方案具有更差的时间复杂性。对于哈希,正如您所使用的那样,时间复杂度确实是O(n)。每次插入都是O(1)并且有n个字,因此插入阶段花费O(n)。迭代并找到最大值则为O(n)。如你所说,空间也是O(n)。

请注意,您无法使用Chris的解决方案提前终止算法,因为搜索哈希表的代价很高,并且无法在每次插入后的O(1)时间内执行此操作。< / p>

堆将花费更多时间,因为您需要在每次插入期间维护堆。堆插入是O(log(n)),因此插入的总成本将为O(nlog(n))。

答案 5 :(得分:0)

如果您正在处理一本书,那么您就知道词汇量和近似词频。即使您没有预先提供此信息,也可以通过扫描随机样本来获得良好的估计值。

对于确切的答案,我会使用k个最常见单词的完美哈希函数。完美的散列函数需要O(k)内存并保证快速最坏情况下的O(1)查找。

对于不常见的单词,我将使用实现为堆或自平衡树的优先级队列。常规哈希表也可能是一个不错的选择。