假设您一直都在获取数字,而您不知道开始会出现多少个数字。在任何时候,我都希望能够立即n/3
输出最大的数字和n
的最大数字(其中(in O(1))
是到目前为止输入的数字的数量)。输入新号码的最长时间为O(log(n))
。
实现此目标的最佳方法是什么?
答案 0 :(得分:3)
您可以用两个heaps来实现它:最小堆(A)和最大堆(B)。
这个想法是将 n / 3 最大值存储在A中,其余的存储在B中,这样B中的最大值就是 n / 3 最大值(从零开始计数)。假设在 n 不是三的倍数的情况下将 n / 3 截断,并且结果值是从零开始的(n/3 == 0
表示最大值也是 n / 3 的最大元素),这意味着必须保持以下不变性:
A.size == floor((A.size + B.size)/3)
...归结为:
0 <= B.size - 2*A.size < 3
上述条件可能会有所不同,具体取决于解释 n / 3 元素是什么(是从0开始,从1开始,四舍五入,被舍入等),还有一些如果集合中的元素少于3个,则可能需要特别注意,具体取决于该定义。
要添加值 v ,您首先要确定它属于哪个堆。如果它小于当前的 n / 3 th 最大值(B中的最大值),则它属于B,否则属于A。在添加之后,不变性可能会被破坏,必须通过从任一堆中提取一个值并将其添加到另一堆中来恢复。
更正式地添加了 v ,如下所示:
if v < B.peek():
B.add(v)
if B.size - 2*A.size >= 3:
A.add(B.pull())
else:
A.add(v)
if B.size - 2*A.size < 0:
B.add(A.pull())
获得 n / 3 th 最大值的实现是:
B.peek()
以上所用方法的时间复杂度为:
要保持整体最大值是一件容易的事。堆仅用于跟踪 n / 3 th 最大值。