Algorithm 从无穷多个数集中的最后k个元素中找出最小的数
你会得到一个无限的数字列表。在最后的k元素中找到 使用最小复杂性的最低元素(基于值) 注意:一旦最小元素不在k-子集中,新的最小元素 需要查明 例如:输入:67 45 12 34 90 83 64 86。。。。k=5 最初(6745123490)将是{k}。随着新输入的到来,{k} 将是(4512349083),(1234908364),(3490836486)。。。 最低元素分别为12、12和34Algorithm 从无穷多个数集中的最后k个元素中找出最小的数,algorithm,Algorithm,你会得到一个无限的数字列表。在最后的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