arrays - 在大小为 N 的数组的每 k 个元素中查找最小和第二小的元素

标签 arrays algorithm

我试图在 N 大小的数组的 k 个元素中找到最小和次小的元素(没有排序和最小/最大堆)。

使用传统的方法,首先从第 0 个元素开始,在前 k 个元素中找到最小和第二小的元素,然后将起点移动 1 并重复该过程。但是它的复杂度是O(Nk)。如果可能,我需要一个复杂度为 O(N) 的解决方案。对此有何建议?

在 Jubobs 的评论后编辑:例如如果说数组是 {12, 1, 78, 90, 57, 89, 56} 并且 k3,那么对于第一个 k 个元素 (12, 1, 78) 最小的元素将是 1,第二小的元素将是 12。对于第二个 k 元素 (1, 78, 90),最小的元素将是 1,第二小的元素将是 78等等。

以下是我用 O(Nk) 复杂度编写的代码片段:

int j=0;
while(j < input.length-k+1) {
    int i=j;
    for(i=j; i < j+k; i++) {
        if(input[i] < min) {
            min2 = min;
            min = input[i];
        } else if(input[i] > min && input[i] < min2) {
            min2 = input[i];    
        }                   
    }
}

最佳答案

您可以使用 deque你保持分类。

在每一步中,如果双端队列中的第一个元素 ( d.front.index ) 早于 k相对于当前步骤的步骤,将其弹出( d.popFront() )。

然后,当数组中当前位置的元素小于双端队列中的最后一个元素(d.back.value)时,从双端队列中弹出元素(d.popBack())。

最后,将当前值添加到双端队列的末尾 ( d.pushBack() )。

在每一步,d.front.value将是 [step - k + 1, step] 的最小值.

您可以将双端队列存储为大小为 k 的链表.然后你将可以随时访问其中的第二个元素,这将是 [step - k + 1, step] 中第二小的元素。 .如果由于弹出每个元素而最终只得到一个元素,则必须小心。在那种情况下,弹出的可能是 future 查询中第二小的。您可以将它们保存在您处理类似于双端队列的另一个列表中,请参见下面的示例。

这是 O(n)摊销,因为数组中的每个元素最多进入和离开双端队列一次。它可能看起来像 O(nk)因为你会有一些嵌套循环,但如果你考虑操作总数,你会发现它实际上是 O(n) .

伪代码

for i = 0, i < n:
    if not d.empty and i - d.front.index >= k:
      d.popFront()
    while not d.empty and d.back.value > a[i]:
      d.popBack()

    d.pushBack({index = i, value = a[i]})

    output d.front.value as the minimum value in [i - k + 1, i]

跟踪第二个最小值的代码留作练习。

以你的例子为例:

a = {12, 1, 78, 90, 57, 89, 56}, k = 3

d = {12}
d = {1} (12 popped, track this)
d = {1, 78} => we have to output smallest and second smallest in [0, 2].
            => smallest is always the first in the deque, so 1
            => second = min(12 [this was popped], 78) = 12
d = {1, 78, 90)
            => smallest 1, second is 78 (12 is too old now)
d = {57}
            => 1 popped for being too old, the others for being too large
            => smallest = 57, second = 78 (last popped)
d = {57, 89}
            => smallest = 57, second = 89
d = {56}
            => smallest = 56, second = 57

基本上,您为第二小的数组保留一个数组。这将包含还不太旧的弹出值。这些也将被排序,但降序排列。

此方法的示例运行,其中 d2是第二个数组:

a = {12, 1, 78, 90, 57, 89, 56} 

d = {12},           d2 = {}
d = {1},            d2 = {12}
d = {1, 78},        d2 = {12}
  => output 1, 12
d = {1, 78, 90},    d2 = {} - 12 was too old
  => output 1, 78
d = {57}            d2 = {90, 78}
  => output 57, 78
d = {57, 89}        d2 = {90} - 78 too old
  => output 57, 89 (if we had 91 instead of 89, we'd have output the 90 in d2)
d = {56}            d2 = {89, 57}
  => output 56, 57

关于arrays - 在大小为 N 的数组的每 k 个元素中查找最小和第二小的元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32425583/

相关文章:

algorithm - 在 n x m 矩阵中放置 k 条不相交路径的方法有多少

algorithm - 差异可视化算法

Java计算排序数组中每个项目的出现次数

c++ - 如何交换二维数组的最大和最小成员索引

c++ - 185000阵列后BFPRT(中位数中值)算法崩溃

algorithm - 差异化更快

performance - 我正在寻找一种用于矩阵 [NxM] 的快速 DCT 和 IDCT 的简单算法

将字符串复制到数组中,然后使用 for 循环打印它

javascript - 使用文本框中的值更新 javascript 数组的值

arrays - 多个 Firestore 快照相互覆盖