【发布时间】:2021-12-06 09:58:06
【问题描述】:
我遇到了这样一个问题:用 push(x)、pop() 和 pop_max() 方法实现一个队列。
pop_max() 函数应该按照 FIFO 规则弹出最大的元素。
例如: pop_max()之前:front-> 2,3,4,5,1,5
pop_max() 之后:front-> 2,3,4,1,5
以下是我的一些尝试。
-
用基本队列实现它,使用支持队列通过 O(n) 扫描找到最大元素。
pop()/push() 是 O(1),pop_max() 应该是 O(n)。
-
用双链表和单调栈实现它。
pop()/pop_max() 是 O(1),push() 应该是 O(n)。
有人知道以最小的时间复杂度做到这一点的方法是什么?我看过这个Implement a queue in which push_rear(), pop_front() and get_min() are all constant time operations,它提供的方法似乎不适合这个场景。
【问题讨论】:
-
对于你的(1.),“基本队列”仍然需要实现;您可以使用单链表来实现它,并且 pop_max 操作可以“作弊”,因为它不尊重队列约束。
-
您可以实现一个带有双链表的队列,以及一个在链表中存储节点的最大堆和一个跟踪当前队列中值频率的计数器。对于重复的元素,不同元素的推送将是对数且恒定的,而 pop 将是恒定的。 pop_max 将按对数摊销,但在某些情况下,需要许多 pop_max(与推送次数成线性关系)来同步堆和计数器。
-
@wLui155 我明白了你的意思,但是 pop 怎么可能保持不变呢?因为您也应该更新最大堆。
-
@sugarfree 它不能。您可以使用您尝试实现的抽象数据类型通过推送整个列表然后重复弹出最大化来进行排序,因此具有通用可比较元素的 O(1) 是不可能的。
-
pop 将删除链表中最早的元素并减少计数器中删除的值。因为它不对堆做任何修改并且由两个恒定时间操作组成,所以它也是恒定的。同时,权衡是使堆赶上队列的当前状态可能会有点慢(当在许多正常的 push/pop 操作之后调用 pop_max 时)。
标签: algorithm data-structures queue stack