我知道以下针对重击手的算法:
Algorithm findHeavyHitters(epsilon, inputStream)
integer k = ceiling(1 / epsilon) - 1
initialize hashmap H of size k
while an item i from the input stream arrives:
if H[i] exists
increment the value associated with H[i]
elsif number of items in H < k
put H[i] into map with value of 1
elseif there exists an entry j with a value of 0
remove j and put H[i] into map with value of 1
else
decrement all values in H by 1
endwhile
return H
如果我错了,请纠正我,但此算法不能在O(n)中运行。是否可以修改此算法,使其在O(n)中运行,同时保持O(1 / epsilon)空间的使用?
对于数据流,算法的要点是返回顶部epsilon * t项。 Epsilon以百分比形式给出(例如,输入0.1表示至少发生10%的数据)。
答案 0 :(得分:1)
算法以平均时间O(n)运行,因为哈希查找平均为O(1)。
有两个实现细节。首先,最后一步似乎涉及触及H中的每个值:
为了生成这个O(1),我们添加了一个额外的存储位置,称为base
,它被初始化为0.然后我们按如下方式修改算法:
while an item i from the input stream arrives:
if H[i] exists
increment the value associated with H[i]
elsif number of items in H < k
put H[i] into map with value of base + 1
elseif there exists an entry j with a value of base
remove j and put H[i] into map with value of base + 1
else
increment base
endwhile
第二个问题是在O(1)中找到值为base
(或0)的条目。这可以通过将元素保存在&#34; comb&#34;:双链表的链表中来完成。每个内部链接列表包含具有特定计数的条目。外链表按计数顺序包含计数列表,头指向具有最小计数的列表。如果您绘制此数据结构,它看起来像梳子:
[ base ] -> entry a -> entry b -> entry c
|
[ base + i ] -> entry d
|
[ base + j ] -> entry e -> entry f
|
etc.
哈希表现在指向条目,而不是包含它们。要增加单个条目的计数,该条目将从其列表中删除(如果列表包含多个元素),并将其插入到下一个列表中或放入单个元素列表中,该列表将在列表之后插入,取决于与下一个列表相关的计数。此操作为O(1)。
梳状数据结构仍为O(k),其中k是散列中元素的数量,因为不能有比元素更多的不同计数。
您可以使用简单数组和每个计数的第一个条目的索引列表,而不是双向链接列表。要将条目移动到下一个计数存储桶,首先将其与具有该计数的最后一个条目交换,然后提前下一个计数列表的开头或插入新的计数列表条目,具体取决于下一个计数列表是否为&#39 ; s计数大于或大于1。要完成交换,有必要更新散列中两个交换条目的位置,但这仍然是O(1)。