Algorithm 在O(1)中实现抽取最小值

Algorithm 在O(1)中实现抽取最小值,algorithm,Algorithm,可能重复: 我需要实现一个FIFO数据结构,其中排队、退队和提取最小值的摊销成本为O(1) 我曾经考虑过使用一个常规队列,它将有O(1)个使用链表排队和出列的队列,然后对于Extract Min,我将遍历数组以获取Min并移除它。但这样做的成本是O(n),而这不会使摊销成本达到O(1) 任何帮助或提示都将不胜感激。您可以使用额外的成员实现队列,该成员始终指向队列中值最小的节点。必须稍微修改Enqueue(),如下伪代码所示: void enqueue( data_t val ) { bac

可能重复:

我需要实现一个FIFO数据结构,其中排队、退队和提取最小值的摊销成本为O(1)

我曾经考虑过使用一个常规队列,它将有O(1)个使用链表排队和出列的队列,然后对于Extract Min,我将遍历数组以获取Min并移除它。但这样做的成本是O(n),而这不会使摊销成本达到O(1)


任何帮助或提示都将不胜感激。

您可以使用额外的成员实现队列,该成员始终指向队列中值最小的节点。必须稍微修改Enqueue(),如下伪代码所示:

void enqueue( data_t val )
{
  back->m_data = val; // back is a pointer to the end of the queue.

  // m_Min should be initialized with a large value.
  if (back->m_data <= m_Min->m_data)
  {
    m_Min = back;
  }
  else // => back->m_data > m_Min->m_data
  {
    swap(back->m_data, m_Min->m_data);
    m_Min = back;
  }
}
因此,当
front
达到
mu Min
时,队列中只有一个元素的值最小

data_t min()
{
  return m_Min->m_data;
}
编辑:从我以前的版本修改了
enqueue()中的if块。enqueue()基本上在末尾推送新值。但如果前一个节点大于当前最小值(保留在队列的最后一个节点中),则与前一个节点交换

例如,如果输入序列为5、3、7、1、4、6、8

1.
front -> 5 <- back
         ^min

2.
front -> 5 3 <- back.
         ^min

front -> 5 3 <- back.
           ^min

3.
front -> 5 3 7 <- back
           ^min

front -> 5 7 3 <- back
             ^min

4.
front -> 5 7 3 1 <- back
             ^min

front -> 5 7 3 1 <- back
               ^min

5.
front -> 5 7 3 1 4 <- back
               ^min

front -> 5 7 3 4 1 <- back
                 ^min

6.
front -> 5 7 3 4 1 6 <- back
                 ^min

front -> 5 7 3 4 6 1 <- back
                   ^min

7.
front -> 5 7 3 4 6 1 8 <- back
                   ^min

front -> 5 7 3 4 6 8 1 <- back
                     ^min
1。

前面->5 5 3 5 3 7 5 7 3 5 7 3 7 1 5 7 3 1 4 5 7 3 1 5 7 3 4 1 6 7 3 4 6 4 6 1 5 7 3 6 1 8 7 3 4 6 6 8 1我建议使用两个基础数据结构一起构建新的结构。

首先,您可以使用普通的出列,它可以在O(1)中提供排队和退队。 然后使用助手堆栈跟踪最小值的指针。 从头开始,插入第一项时,只需deq.push_back()和stack.push(); 然后针对以下项目:

排队:推回以退出队列,将新项目与堆栈顶部进行比较,如果新项目较小,则推回,否则不执行任何操作。O(1)中的所有操作
出列:获取出列的头部,将其与堆栈顶部进行比较,如果它们相同,则表示出列的是最小的,然后弹出堆栈,否则,对堆栈不执行任何操作。O(1)中的所有操作。

Extract Min:获取堆栈的顶部,它是最小的。O(1)

您必须在我想说的某个地方保留一个排序列表。提示:您可以向数据结构中添加什么,以避免必须遍历列表才能找到最小值?我可以保留一个指向最小值元素的指针,但我必须在每次提取时重新计算它们。另一种方法是将排序数组作为附加列表保留并添加到列表中。插入它将花费lgn时间,但提取min将是O(1)。这仍然不会导致所有运营的摊余成本保持不变think@Tom-如果优先级队列依赖于比较,则要求您执行的操作是不可能的,因为它提供了一种基于O(n)比较的排序算法(填充队列,然后保持最小值的出列)。关于你的输入,你还知道什么吗,比如它是否是积分?如果没有关于元素的额外知识,你将无法解决这个问题。输入将是整数。不需要使用数组,我可以自由使用任何数据结构或创建自己的数据结构。我认为这不会给您提供您想要的保证-特别是,在您将最小元素出列后,如何更新摊销O(1)中的最小指针?我指的是队列。我的错。更正了这个问题。@templatetypedef:很好。只有删除值最小的节点时,问题才会出现。在这种情况下,将有O(n)的开销。没有O(n)开销,我不知道如何确定序列的最小值!?!?这不会改变顺序并违反FIFO规则吗。你的插入顺序是5 3 7。。。。但出列将给出5 7 3…?是的,它确实修改了队列,但所有(入列/出列/提取分钟)操作都在O(1)时间内完成。我认为提取分钟需要删除最小的元素,而不仅仅是返回其值。否则,这是前面问题的重复。假设你插入4,5,8,7。堆栈中只有4个,而deque将有4、5、8、7个。如何处理O(1)摊销中连续两次的extract-min()操作?是的,这不是一个解决方案。在我发布这篇文章之后,我也意识到它是不正确的(我认为对于Extract min,它本身没有O(1)解决方案,就像堆排序一样,它在n*log(n)中运行。我认为没有O(1)解决方案。。
1.
front -> 5 <- back
         ^min

2.
front -> 5 3 <- back.
         ^min

front -> 5 3 <- back.
           ^min

3.
front -> 5 3 7 <- back
           ^min

front -> 5 7 3 <- back
             ^min

4.
front -> 5 7 3 1 <- back
             ^min

front -> 5 7 3 1 <- back
               ^min

5.
front -> 5 7 3 1 4 <- back
               ^min

front -> 5 7 3 4 1 <- back
                 ^min

6.
front -> 5 7 3 4 1 6 <- back
                 ^min

front -> 5 7 3 4 6 1 <- back
                   ^min

7.
front -> 5 7 3 4 6 1 8 <- back
                   ^min

front -> 5 7 3 4 6 8 1 <- back
                     ^min
front -> 4 6 8 1 <- back
               ^min