仅使用O(k)内存O(n)时间从给定序列中找到第k个最小元素


11

假设我们逐个读取数字的序列。如何仅使用单元存储器并在线性时间()中找到第个最小元素。我认为我们应该保存序列的前项,并在获得第个项时删除一个确定不能为第个最小元素的项,然后保存第个项。因此,我们应该有一个指标,在每个步骤中显示此不可用的术语,并且该指标应在每个步骤中快速更新。我从“ max”开始;但它无法快速更新;意味着如果我们考虑最大ķnkO n k k + 1 k k + 1O(k)O(n)kk+1kk+1然后在第一次删除中,我们错过了最大值,我们应该在搜索最大值,其原因时间不是线性的。也许我们应该更智能地保存前项。O(k)k(nk)×O(k)k

我该如何解决这个问题?


1
您是否对在线算法感兴趣,或者任何算法都可以?
Yuval Filmus

如果k=θ(n)则可以使用阶数统计算法来实现。如果k=o(n)则可以使用任何高度平衡的树来执行O(k)内存和O(nlogk)时间。
Shreesh


有线性时间就地算法,您可以在Google上搜索,但是它们有些复杂。
Yuval Filmus

@ xavierm02不是完全相同的选择问题。因为存在内存限制约束。
Shahab_HK

Answers:


16

创建大小为的缓冲区。从数组中读取2 k个元素。使用 线性时间选择算法对缓冲区进行分区,以使k个最小的元素排在最前面;这需要O k 时间。现在,将数组中的另外k个项目读入缓冲区,替换缓冲区中k个最大的项目,像以前一样对缓冲区进行分区,然后重复。2k2kkO(k)kk

这需要时间和O k 空间。O(kn/k)=O(n)O(k)


+1,适合要求的渐近性。话虽如此,但我不认为这比执行单个线性时间选择算法要快...除了很小时,它提供了一个有趣的视角。例如,对于k = 1,此算法将产生函数。kk=1min
orlp

1
有时,线性时间选择算法会占用太多空间。例如,它不适用于流上下文或输入数组是不可变的。
jbapple

这些是有效点。
Orlp

3

您可以在内存和O n log k 时间中做到这一点,方法是从O k 时间中的前k个元素形成固定大小的最大堆,然后遍历数组的其余部分并推入新的元素,然后为每个元素弹出O log k ,得到总时间O k + n log k = O n log k O(k)O(nlogk)kO(k)O(logk)O(k+nlogk)O(nlogk)

通过使用中位数中值选择算法,在k处选择并返回前k个元素,可以在辅助存储器和O n 时间中进行操作。在不改变渐近性的情况下,您可以使用introselect加快平均情况。这是解决问题的规范方法。O(logn)O(n)kk

现在从技术上讲O k 是无法比拟的。但是我认为O log n 在实践中更好,因为考虑到没有计算机系统具有超过2 64个字节的内存,log 2 64 = 64,它实际上是恒定的。同时,k可以增长到与n一样大。O(logn)O(k)O(logn)264log264=64kn


请注意,可以通过在有趣的时候反转堆使用的顺序,将基于堆的算法的复杂度提高到O(n×logmin(k,nk))
xavierm02'1

@ xavierm02 = Ô ķ 。证明:k的最坏情况是nm i n k n - k 的最坏情况是nO(min(k,nk))O(k)knmin(k,nk)。它们是一个常数因子内的相同,从而 øÑķÑ-ķ=Ôķn2O(min(k,nk))O(k)
Orlp

@ xavierm02话虽如此,它仍然是一个不错的
加速器

O k 但不是 O min k n - k 。假设是。再有就是一些 Ç和一些中号,使得对于每个中号ķ Ñ,我们有 ķ Ç ñ - ķ ,这显然是错误的(因为我们可以采取 Ñ = ķ + un,k=kO(k)O(min(k,nk))CMMknkC(nk)n=k+). 所以O(min(k,nk))O(k)
xavierm02 '01 / 01/17

@ xavierm02我不熟悉你的符号。公平地讲,我通常不太熟悉多维big- O表示法,尤其是考虑到n k并非无关紧要。un,kOn,k
orlp
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.