带有链表的哈希表中的前 10 个频率
Posted
技术标签:
【中文标题】带有链表的哈希表中的前 10 个频率【英文标题】:Top 10 Frequencies in a Hash Table with Linked Lists 【发布时间】:2010-10-25 02:10:48 【问题描述】:下面的代码会将它在我的哈希表(其中是一堆链表)中可以找到的最高频率打印 10 次。我需要我的代码来打印我的哈希表中的前 10 个频率。我不知道该怎么做(代码示例会很棒,简单的英语逻辑/伪代码也一样好)。
-
我创建了一个名为“tmp”的临时哈希列表,它指向我的哈希表“hashtable”
while 循环然后遍历列表并查找最高频率,即 int 'tmp->freq'
循环将继续使用变量“topfreq”复制它找到的最高频率的过程,直到它到达哈希表上链表的末尾。
我的“节点”是一个由变量“freq”(int)和“word”(128 个字符)组成的结构。当循环没有其他内容可搜索时,它会在屏幕上打印这两个值。
问题是,我不知道如何从我刚刚找到的数字中找到下一个最小的数字(这可能包括另一个具有相同频率值的节点,所以我必须检查那个词也不一样)。
void toptenwords()
int topfreq = 0;
int minfreq = 0;
char topword[SIZEOFWORD];
for(int p = 0; p < 10; p++) // We need the top 10 frequencies... so we do this 10 times
for(int m = 0; m < HASHTABLESIZE; m++) // Go through the entire hast table
node* tmp;
tmp = hashtable[m];
while(tmp != NULL) // Walk through the entire linked list
if(tmp->freq > topfreq) // If the freqency on hand is larger that the one found, store...
topfreq = tmp->freq;
strcpy(topword, tmp->word);
tmp = tmp->next;
cout << topfreq << "\t" << topword << endl;
非常感谢任何和所有帮助:)
【问题讨论】:
请将您的制表符更改为 4 个空格。那会伤害眼睛,而且护目镜什么都不做! ;) 如果这是作业添加作业标签。 @all:这是我自己设定的任务......所以不,不是家庭作业。 【参考方案1】:做到这一点的绝对最快方法是使用SoftHeap。使用 SoftHeap,您可以在 O(n) 时间内找到前 10 个项目,而此处发布的所有其他解决方案都需要 O(n lg n) 时间。
http://en.wikipedia.org/wiki/Soft_heap
这篇***文章展示了如何使用软堆在 O(n) 时间内找到中位数,前 10 名只是中位数问题的一个子集。然后,您可以按顺序对前 10 项中的项目进行排序,并且由于您总是最多对 10 项进行排序,因此仍然是 O(n) 时间。
【讨论】:
【参考方案2】:如果目标是累积单词频率,则包含单词链接列表的哈希表似乎是一种特殊的数据结构。
尽管如此,获得 10 个最高频率节点的有效方法是将每个节点插入优先级队列/堆,例如斐波那契堆,其插入时间为 O(1),删除时间为 O(n)。假设哈希表迭代很快,该方法的运行时间为 O(n×O(1) + 10×O(n)) ≡ O(n)。
【讨论】:
+1。两件事:我知道的所有堆(包括斐波那契)的删除时间是 O(log n),而不是 O(n);如果 n >> 10 保持最小堆(而不是最大堆)会快得多,您可以在其中插入下一个元素并在每次迭代中删除最小的元素(这样堆最多只包含10 个元素)。 O(log(n)) ≡ O(n) 是它的摊销删除时间 - 因为这个算法只涉及删除一些摊销分析无效的键。斐波那契堆的最坏情况删除是 O(n)。 好点。所以在一般情况下,当我们想要前 k 个频率时,您的算法将是 O(kn) - 即与运行选择排序的前 k 个迭代的明显解决方案相同。请参阅我对使用普通二进制堆(而不是斐波那契堆,实现起来更棘手)的 O(nlog(k)) 算法的回答。【参考方案3】:假设总共有 n 个词,我们需要出现频率最高的 k 个词(这里,k = 10)。
如果 n 比 k 大很多,我知道的最有效的方法是维护一个最小堆(即顶部元素具有 minimum 堆中所有元素的频率)。在每次迭代中,将下一个频率插入堆中,如果堆现在包含 k+1 个元素,则删除 最小的。这样,堆始终保持在 k 个元素的大小,在任何时候都包含到目前为止看到的 k 个最高频率的元素。在处理结束时,按递增顺序读出 k 个最高频率的元素。
时间复杂度:对于每个n个单词,我们做两件事:插入一个大小最多为k的堆中,然后删除最小元素。每个操作花费 O(log k) 时间,因此整个循环花费 O(nlog k) 时间。最后,我们从一个大小为 k 的堆中读出 k 个元素,耗时 O(klog k),总时间为 O((n+k )日志 k)。由于我们知道 k n,O(klog k) 最坏的情况是 O(nlog k),所以这可以简化为 O(nlog k)。
【讨论】:
【参考方案4】:第 1 步(低效):
通过插入排序将向量移动到已排序的容器中,但插入到大小为 10 的容器(例如链表或向量)中,并删除掉出列表底部的所有元素。
第 2 步(高效):
与步骤 1 相同,但要跟踪列表底部项的大小,如果当前项太小,则完全跳过插入步骤。
【讨论】:
【参考方案5】:保留一个包含 10 个节点指针的数组,并将每个节点插入到数组中,保持数组有序。数组中的第 11 个节点在每次迭代时都会被覆盖并包含垃圾。
void toptenwords()
int topfreq = 0;
int minfreq = 0;
node *topwords[11];
int current_topwords = 0;
for(int m = 0; m < HASHTABLESIZE; m++) // Go through the entire hast table
node* tmp;
tmp = hashtable[m];
while(tmp != NULL) // Walk through the entire linked list
topwords[current_topwords] = tmp;
current_topwords++;
for(int i = current_topwords - 1; i > 0; i--)
if(topwords[i]->freq > topwords[i - 1]->freq)
node *temp = topwords[i - 1];
topwords[i - 1] = topwords[i];
topwords[i] = temp;
else break;
if(current_topwords > 10) current_topwords = 10;
tmp = tmp->next;
【讨论】:
【参考方案6】:在遍历哈希表(然后遍历其中包含的每个链表)时,将自平衡二叉树 (std::set) 作为“结果”列表。当您遇到每个频率时,将其插入到列表中,如果列表中的条目超过 10 个,则将其截断。完成后,您将获得一组(排序列表)前十个频率,您可以根据需要对其进行操作。
在哈希表本身中使用集合而不是链表可能会获得性能提升,但您可以自己解决。
【讨论】:
作为要求之一,我不能使用二叉树。非常感谢您的建议,非常感谢! @tmhai:“要求”之一是不允许使用二叉树?这是作业吗? (如果是这样,很好,但这样标记你的问题。)【参考方案7】:我会维护一组已使用的单词并更改最里面的 if 条件以测试频率是否大于先前的最高频率和 tmp->单词不在已使用的单词列表中。
【讨论】:
以上是关于带有链表的哈希表中的前 10 个频率的主要内容,如果未能解决你的问题,请参考以下文章
Leetcode练习(Python):哈希表类:第138题: 给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。 要求返回这个链表的 深拷贝。 我们用一个