在O(n)中返回文档中10个最常用的单词

时间:2012-10-25 21:37:20

标签: java algorithm complexity-theory time-complexity

如何设计一种算法,可以在O(n)时间内返回文档中最常用的10个单词?如果可以使用额外的空间。

我可以解析并将单词放在带有count的哈希映射中。但接下来我必须对值进行排序以获得最常见的值。另外我必须有一个映射btw值 - >由于值可能重复而无法维护的密钥。

那我怎么解决这个问题呢?

5 个答案:

答案 0 :(得分:5)

这是一个简单的算法:

  • 通过文档一次读一个字。的 O(n)的
  • 使用每个单词构建HashTable。的 O(n)的
    • 使用单词作为键。的 O(1)
    • 使用您将此单词视为值的次数。的 O(1)
    • (例如,如果要将密钥添加到哈希表中,则值为1;如果已在哈希表中使用密钥,则将其关联值增加1) O(1)
  • 创建一对大小为10的数组(即字符串字[10] / int count [10],或使用< Pair>),使用此对来跟踪10个最常用的单词及其单词计入下一步。的 O(1)
  • 迭代完成的HashTable一次: O(n)
    • 如果当前单词的字数高于数组对中的条目数,则替换该特定条目并将所有内容向下移动1个插槽。的 O(1)
  • 输出这对数组。的 O(1)

O(n)运行时。

O(n)存储HashTable +数组

(旁注:您可以将HashTable视为字典:存储密钥的方法:密钥唯一的值对。技术上HashMaps意味着异步访问,而HashTable意味着同步。)

答案 1 :(得分:2)

如果使用正确的数据结构,可以在O(n)中完成。

考虑一个Node,由两件事组成:

  • 一个计数器(最初设置为0)。
  • 指向Node的255个(或任意数量的字符)指针的数组。所有指针最初都设置为NULL

创建根节点。定义“当前”Node指针,最初将其设置为根节点。 然后遍历文档的所有字符并执行以下操作:

  • 如果下一个字符不是空格 - 从当前节点的数组中选择适当的指针。如果是NULL - 分配它。当前Node指针已更新。
  • 如果它是一个空格(或任何单词分隔符) - 增加“当前”Node的计数器。然后重置“当前”Node指针以指向根节点。

通过这样你在O(n)中构建一棵树。每个元素(节点和离开)都表示一个特定的单词及其计数器。

然后横向树以找到具有最大计数器的节点。它也是O(n),因为树中元素的数量不大于O(n)。

<强>更新

最后一步不是强制性的。实际上,在字符处理期间可以更新最常用的单词。 以下是伪代码:

struct Node
{
    size_t m_Counter;
    Node* m_ppNext[255];
    Node* m_pPrev;

    Node(Node* pPrev) :m_Counter(0)
    {
        m_pPrev = pPrev;
        memset(m_ppNext, 0, sizeof(m_ppNext));
    }
    ~Node()
    {
        for (int i = 0; i < _countof(m_ppNext) i++)
            if (m_ppNext[i])
                delete m_ppNext[i];
    }

};

Node root(NULL);
Node* pPos = &root;
Node* pBest = &root;
char c;

while (0 != (c = GetNextDocumentCharacter()))
{
    if (c == ' ')
    {
        if (pPos != &root)
        {
            pPos->m_Counter++;

            if (pBest->m_Counter < pPos->m_Counter)
                pBest = pPos;

            pPos = &root;
        }
    } else
    {
        Node*& pNext = pPos->m_ppNext[c - 1];
        if (!pNext)
            pNext = new Node(pPos);
        pPos = pNext;
    }
}

// pBest points to the most common word. Using pBest->m_pPrev we iterate in reverse order through its characters

答案 2 :(得分:2)

最快的方法是使用基数树。您可以将字数存储在基数树的叶子上。保留10个最常用单词及其出现次数的单独列表以及存储进入此列表所需的阈值的变量。将项目添加到树中时更新此列表。

答案 3 :(得分:0)

维护(字,计数)的地图将是O(n)。

构建地图后,迭代密钥并检索十个最常用的密钥。

O(n)+ O(n)

- 但是对于所需的额外外部存储器的这种解决方案并不完全满意。

答案 4 :(得分:0)

我会使用ArrayList和HashTable。

这是我想到的算法,

Loop through all the word in the document.

if (HashTable.contains(word) )
    increment count for that word in the HashTable;
else
    ArrayList.add(word);
    HashTable.add(word);
    word count in HashTable = 1;

循环完整整个文档后,

Loop through ArrayList<word>
    Retrieve the word count for that word from the HashTable;
    Keep a list of the top 10 words;

运行时应该是O(n)来构造HashTable和ArrayList。 排在前10位的列表应该是O(m),其中m是唯一字的数量。 O(n + m)其中n> m>&gt;为O(n)