Algorithm 从无穷多个数集中的最后k个元素中找出最小的数

Algorithm 从无穷多个数集中的最后k个元素中找出最小的数,algorithm,Algorithm,你会得到一个无限的数字列表。在最后的k元素中找到 使用最小复杂性的最低元素(基于值) 注意:一旦最小元素不在k-子集中,新的最小元素 需要查明 例如:输入:67 45 12 34 90 83 64 86。。。。k=5 最初(6745123490)将是{k}。随着新输入的到来,{k} 将是(4512349083),(1234908364),(3490836486)。。。 最低元素分别为12、12和34 有人知道如何解决这个问题吗?看看。每个节点有两个键:第一个键是二进制搜索树,第二个键是堆。可以使

你会得到一个无限的数字列表。在最后的k元素中找到 使用最小复杂性的最低元素(基于值)

注意:一旦最小元素不在k-子集中,新的最小元素 需要查明

例如:输入:67 45 12 34 90 83 64 86。。。。k=5

最初(6745123490)将是{k}。随着新输入的到来,{k} 将是(4512349083),(1234908364),(3490836486)。。。 最低元素分别为12、12和34


有人知道如何解决这个问题吗?

看看。每个节点有两个键:第一个键是二进制搜索树,第二个键是堆。可以使用第一个键存储元素的编号,使用第二个键存储元素的值。您将能够在
O(log(k))
time中添加新元素,在
O(log(k))
time中删除旧元素,并在常量时间中以最小值获取元素。

您可以在最小堆和链表之间实现混合结构。每个堆元素都有到下一个元素的链接。你留着头和尾巴。在尾部添加元素,同时从堆中删除head元素

每个元素将在
O(log k)
时间内处理

下面是Python中的一个示例:

class BoundedMinTracker:
    def __init__(self, k):
        self._k = k
        self._index = 0
        self._deque = collections.deque()

    def update(self, el):
        while self._deque and self._deque[0][4] >= el:
            self._deque.popleft()
        self._deque.appendleft((self._index, el))
        self._index += 1
        if self._deque[-1][0] < self._index - self._k:
            self._deque.pop()

    def get(self):
        return self._deque[-1][5]
输出:

Pushed:  2, Popped: None, Minimum (last 3):  2
Pushed:  1, Popped: None, Minimum (last 3):  1
Pushed:  3, Popped: None, Minimum (last 3):  1
Pushed:  4, Popped:    2, Minimum (last 3):  1
Pushed:  2, Popped:    1, Minimum (last 3):  2
Pushed: -4, Popped:    3, Minimum (last 3): -4
Pushed:  3, Popped:    4, Minimum (last 3): -4
代码:

类节点:
定义初始化(self、value、index、next):
自我价值=价值
self.index=索引
self.next=下一个
类LinkedHeap:
定义初始化(自):
self.V=[]
self.head=self.tail=Node(无,-1,无)
def计数(自身):
返回len(self.V)
def最小值(自身):
返回(self.V[0]。如果self.count()>0,则返回值,否则为无)
def推送(自身、值):
节点=节点(值,len(self.V),无)
self.tail.next=节点
self.tail=节点
self.V.append(节点)
self.bubble_up(len(self.V)-1)
def pop(自我):
如果不是len(self.V):返回None
node=self.head.next
self.head.next=node.next
self.V[node.index]=self.V[-1]
self.V[node.index].index=node.index
self.V.pop()
self.bubble\u down(node.index)
返回node.value
def气泡上升(自身,n):
而n!=0和自减(n,(n-1)/2):
自交换(n,(n-1)/2)
n=(n-1)/2
def气泡下降(自身,n):
而self.less(n*2+1,n)或self.less(n*2+2,n):
c=自身最小值(n*2+1,n*2+2)
自交换(n,c)
n=c
无def(自身、a、b):
如果a>=self.count():返回False
如果b>=self.count():返回True
返回self.V[a]。值3其他无
如果T.count()大于3:
T.pop()
打印“推式:{:2},弹出式:{:4},最小值(最后3个):{:2}”。格式(数字,弹出式,T.Minimum())

对于这个问题,有一个简单的摊销O(1)算法

实际上,问题是实现一个大小为k的队列(摊销)O(1)访问其最小值。有了这样一个队列,我们可以处理无限量的输入,首先用k个Put操作填充队列,然后在放入新元素之前移出最旧的元素来维护队列

让我们从另一个更简单的问题开始:O(1)访问权达到其最小值的堆栈。那是微不足道的。我们需要做的就是保持一个成对的堆栈:每个堆栈元素由一个数据值和该点的最小值组成。然后,堆栈的最小值是与堆栈顶部元素关联的最小值

现在,快速转移注意力。如果我们只有一个堆栈数据类型,如何有效地实现队列数据类型?答案是所谓的“银行家队列”,它使用两个堆栈,我将其称为
传入
传出
<代码>输入只需按入即可实现<代码>移位通过弹出
传出
来实现,除非它是空的。当
传出
为空时,我们通过依次从
传入
弹出每个元素并将其推到
传出
上来重新填充它,这具有将
传入
反转为
传出
的效果

银行家队列在这两个操作中都是O(1)摊销的,因为(从长远来看),每个元素被推送和弹出两次,每个堆栈中一次。这是因为每隔一段时间(如果队列大小固定,则每
k
次操作),整个元素堆栈都会反转,但平均值为O(1)

现在,对于原始问题,我们有了一个完整的摊销O(1)解决方案:我们使用一个带有两个minstack的银行家队列。那么,在任何给定时刻,队列的最小值就是两个最小值的最小值

一旦我们有了总体思路,就有可能提出一些优化。首先,因为我们知道队列的总大小,所以可以使用循环缓冲区来包含这两个堆栈。我们可以通过向后存储其中一个堆栈来避免反向操作(尽管我们仍然需要重新计算堆栈最小值)。最后,我们可以节省存储空间,因为我们实际上不需要输入<代码>的MinStack-,我们只需要知道它的当前最小值-,我们不需要在输出<代码>中存储值,我们只需要存储最小值

把所有的东西放在一起,一个简单的C++实现:

template<typename value_type>
class MinBuffer {
  private:
    std::vector<value_type> queue_;
    value_type incoming_min_;
    int index_;
  public:
    MinBuffer(int capacity)
      : queue_(capacity + 1, std::numeric_limits<value_type>::max()),
        incoming_min_(std::numeric_limits<value_type>::max()),
        index_(0) {
      assert(capacity > 0);
    };
    void push_back(const value_type& val) {
      if (index_ == queue_.size() - 1) {
        while (--index_)
          queue_[index_] = std::min(queue_[index_], queue_[index_ + 1]);
        incoming_min_ = std::numeric_limits<value_type>::max();
      }
      queue_[index_++] = val;
      incoming_min_ = std::min(val, incoming_min_);
    }
    value_type getmin() const {
      return std::min(incoming_min_, queue_[index_]);
    }
};
模板
类最小缓冲区{
私人:
向量队列;
值\输入类型\最小值\;
int索引;
公众:
最小缓冲区(整数容量)
:queue_(容量+1,标准::数值_限制::max()),
传入的\u最小值(标准::数值\u限制::最大值()),
索引(0){
断言(容量>0);
};
无效回推(常量值类型和值){
如果(索引==队列大小()-1){
而(--index_389;)
队列[index][uuuz]=std::min(队列[index][uuuz],队列[index][uuu1]);
inco