如何设计一种算法,可以在O(n)时间内返回文档中最常用的10个单词?如果可以使用额外的空间。
我可以解析并将单词放在带有count的哈希映射中。但接下来我必须对值进行排序以获得最常见的值。另外我必须有一个映射btw值 - >由于值可能重复而无法维护的密钥。
那我怎么解决这个问题呢?
答案 0 :(得分:5)
这是一个简单的算法:
O(n)运行时。
O(n)存储HashTable +数组
(旁注:您可以将HashTable视为字典:存储密钥的方法:密钥唯一的值对。技术上HashMaps意味着异步访问,而HashTable意味着同步。)
答案 1 :(得分:2)
如果使用正确的数据结构,可以在O(n)中完成。
考虑一个Node
,由两件事组成:
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)