0

我知道以下针对重击手的算法:

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 以百分比形式给出(例如,对于至少出现 10% 的时间的数据,输入 0.1)。

4

1 回答 1

1

该算法在平均时间 O(n) 中运行,基于哈希查找平均为 O(1)。

有两个实现细节。首先,最后一步似乎涉及触及 H 中的每个值:

  • 将 H 中的所有值减 1

为了使这个 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)的条目。这可以通过将元素保持在“梳子”中来完成:双向链表的链表。每个内部链表都包含具有特定计数的条目。外部链表包含按计数顺序排列的计数列表,头部指向计数最小的列表。如果你画出这个数据结构,它看起来就像一个梳子:

[  base    ] -> entry a -> entry b -> entry c
    |
[ base + i ] -> entry d
    |
[ base + j ] -> entry e -> entry f
    |
   etc.

哈希表现在指向条目,而不是包含它们。为了增加单个条目的计数,将该条目从其列表中删除(如果列表包含多个元素)并插入到下一个列表中或放入一个单元素列表中,该列表插入到它所在的列表之后,取决于与下一个列表关联的计数。这个操作是 O(1)。

梳状数据结构仍然是 O(k),其中 k 是散列中元素的数量,因为不能有比元素更多的不同计数。

您可以使用一个简单的数组和一个包含每个计数的第一个条目的索引列表来代替双向链表。要将条目移动到下一个计数存储桶,首先将其与具有该计数的最后一个条目交换,然后根据下一个计数列表的计数是否推进下一个计数列表的开头或插入一个新的计数列表条目大于或大于一。为了完成交换,需要更新哈希中两个交换条目的位置,但这仍然是 O(1)。

于 2016-06-16T06:57:31.660 回答