您将获得无限的号码列表。在最后的k个元素中找到 使用最小复杂度的最低元素(基于值)。
注意:一旦最小元素不在k子集中,则为新的最小元素 需要找到。
例如:输入:67 45 12 34 90 83 64 86 .... k = 5
最初(67 45 12 34 90)将是{k}。随着新输入的进入,{k} 将是(45 12 34 90 83),(12 34 90 83 64),(34 90 83 64 86)...... 最低元素分别为12,12和34。
任何人都知道如何解决这个问题?
答案 0 :(得分:2)
这个问题有一个简单的摊销O(1)算法。
实际上,问题是实现大小为k的队列,其中(摊销)O(1)访问其最小值。使用这样的队列,我们可以通过首先使用k Put操作填充队列来处理无限输入,然后通过在放置新元素之前移出最旧的元素来维护队列。
让我们从一个不同的,更简单的问题开始:一个O(1)访问其最小值的堆栈。那是微不足道的。我们需要做的就是保持一堆对:每个堆栈元素由一个数据值和该点的最小值组成。然后堆栈的最小值是与堆栈顶部元素关联的最小值。
现在,快速转移。如果我们只有堆栈数据类型,我们如何有效地实现队列数据类型?答案是所谓的"银行家队列",它使用两个堆栈,我称之为incoming
和outgoing
。只需按下Put
即可实现incoming
。 Shift
是通过弹出outgoing
来实现的,除非它是空的。如果outgoing
为空,我们会通过从incoming
依次弹出每个元素并将其推送到outgoing
来重新填充,这会将incoming
转换为outgoing
}。
银行家的队列在两个操作中都摊销了O(1),因为(从长远来看),每个元素都被推送并弹出两次,每次从每个堆栈中弹出一次。它是摊销的,因为每隔一段时间(如果队列是固定大小,每k
次操作),整个堆栈的元素都会被反转,但这个平均值为O (1)。
现在,我们对原始问题有一个完整的摊销O(1)解决方案:我们使用银行家的队列和两个minstacks。然后,在任何给定时刻,队列的最小值是两个minstack的最小值。
一旦我们有了一般的想法,就有可能提出几个优化。首先,由于我们知道队列的总大小,我们可以使用循环缓冲区来包含两个堆栈。我们可以通过向后存储其中一个堆栈来避免反向操作(尽管我们仍然需要重新计算堆叠的最小值)。最后,我们可以通过注意到我们实际上不需要incoming
的MinStack来节省存储空间 - 我们只需知道它当前的最小值 - 而且我们不需要存储值outgoing
,我们只需存储最小值。
将所有这些放在一起,一个简单的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_]);
}
};
注意:
上述代码使用最大可能值类型作为哨兵,这是Sedgewick的最佳传统;内部队列的最后一个元素始终是sentinel值,它是min
函数的标识元素。这节省了大量繁琐的检查和特殊情况。
内部队列的前部(最多但不包括位置index_
)是incoming
堆栈,顶部位于最高索引处。接下来,直到队列结束,是来自outgoing
堆栈的最小值,以相反的顺序存储,以便顶部位于index_
。如果outgoing
为空,则index_
为k
,queue_[k]
始终为哨兵值。
答案 1 :(得分:2)
您也可以在 O(1)分摊时间内通过使用元素及其索引维护deque来执行此操作。
当你看到一个新元素时:
使用这个维护程序,deque总是按元素从右到左排序(即,最右边的元素是最小的),并从左到右按索引排序(因为从左边添加新元素)。
所以最近的最小元素是双端队列中最右边的元素。
(更新:所以我似乎想出了这个算法:link。由@Niklas B.提供的链接
这是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]
此方法在 O(1)转化时间内进行更新(每个元素仅在deque中添加和删除一次),最差内存使用 O(k),但它通常用得少得多(它不会存储太大而不能成为最小值的元素)
答案 2 :(得分:1)
您可以在最小堆和链表之间实现混合结构。每个堆元素都有一个指向下一个元素的链接。你保持头部和尾部。您可以在尾部添加元素,同时从堆中删除head元素。
每个元素都将在O(log k)
时间内处理。
这是Python中的一个例子:
输出:
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
代码:
class Node:
def __init__(self, value, index, next):
self.value = value
self.index = index
self.next = next
class LinkedHeap:
def __init__(self):
self.V = []
self.head = self.tail = Node(None, -1, None)
def count(self):
return len(self.V)
def minimum(self):
return (self.V[0].value if self.count() > 0 else None)
def push(self, value):
node = Node(value, len(self.V), None)
self.tail.next = node
self.tail = node
self.V.append(node)
self.bubble_up(len(self.V)-1)
def pop(self):
if not len(self.V): return 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_down(node.index)
return node.value
def bubble_up(self, n):
while n != 0 and self.less(n, (n-1)/2):
self.swap(n, (n-1)/2)
n = (n-1)/2
def bubble_down(self, n):
while self.less(n*2+1, n) or self.less(n*2+2, n):
c = self.min(n*2+1, n*2+2)
self.swap(n, c)
n = c
def less(self, a, b):
if a>=self.count(): return False
if b>=self.count(): return True
return self.V[a].value<self.V[b].value
def min(self, a, b):
return (a if self.less(a,b) else b)
def swap(self, a, b):
self.V[a], self.V[b] = self.V[b], self.V[a]
self.V[a].index = a
self.V[b].index = b
L = [2, 1, 3, 4, 2, -4, 3]
T = LinkedHeap()
for number in L:
T.push(number)
popped = T.pop() if T.count() > 3 else None
if T.count() > 3:
T.pop()
print 'Pushed: {:2}, Popped: {:4}, Minimum (last 3): {:2}'.format(number, popped, T.minimum())