By request, here’s a somewhat detailed answer on why I believe that
there’s a solution with worst-case O(1) pushes and pops and O(log n)
pop-maxes. It’s freaking complicated, and you don’t need to understand
it for interviews. Really. I’m writing this answer mostly to entertain
the [algorithm] tag regulars.
Variables
n is the number of elements currently in the structure, and p is the
number of pushes in the lifetime of the structure. Clearly n ≤ p, and in
general, log p is not O(log n).
Tournament trees
The main building block is the tournament tree. A tournament tree is a
full binary tree (every node has zero or two children) with labeled
nodes such that each node with two children labeled x and y is labeled
max(x, y). Semantically, the contents of this data structure are the
labels of nodes with zero children (leaves). If you’re confused, look at
a complete bracket for a single-elimination tournament.
The useful thing about tournament trees is that we can order the leaves
any which way we want. For this problem, we want queue order. The root
element gives the overall max label. To find the leftmost leaf with that
label, repeatedly descend to the left child if it’s labeled the same as
the current node, else the right node. To soft-delete a leaf, set its
value to −∞ and update its ancestors from parent to root.
Amortized O(1) pushes and worst-case O(log p) pop-maxes
There are better ways to accomplish this in practice, but our goal here
is to demonstrate ideas.
We keep a linked list of O(log p) tournament trees. Concatenated, their
leaves represent the queue. Each tree is a complete binary tree with
2k leaves (soft-deleted elements are included in the count)
for some integer k ≥ 0.
The push operation resembles adding one to a number in binary
representation. We put the new element in a tournament tree by itself
and append that tree to the list. While the last two trees in the list
have the same size, combine them into a single tree by making the second
to last the left child and the last the right child of a new tree.
The pop-max operation scans the tree roots to find the overall max, then
soft-deletes the leftmost occurrence.
Worst-case O(1) pushes
We can be lazier about merging trees. Instead of finishing the merge
loop immediately, we keep a queue of continuations. Each continuation
can be represented as a mutable pointer to a tree in the list. To step
it, we compare the size of the tree to the size of its left neighbor; if
they’re the same, then merge the trees and update the pointer to the
merged tree. Otherwise, the continuation is done.
The push operation appends a singleton tree, appends a continuation
pointing to that tree to the back of the queue, and then continues the
work at the front for a couple steps. At any given time, there will be
O(log p) merges to be continued, so pop-max still runs fast enough.
(This follows from the amortized analysis.)
Regular pops
We can implement the pop operation in time worst-case time O(log p) by
adding a doubly linked list structure to the tournament tree leaves not
yet deleted. The tournaments use soft deletion; this list uses hard
deletion.
Obviously we want pops to run in constant time. We can get constant
amortized time by splitting the leftmost tournament tree until it has
one element before soft-deleting (with some sort of barrier to ensure
that the merging continuations from before leave this prefix alone).
Worst-case constant time should be possible with more scheduling like we
did for push.
Worst-case O(log n) pop-maxes
Never mind hand-waving, at this point it’s basically my whole arms. Our
strategy is to limit the effective value of p to O(n) by periodically
rebuilding the whole structure in the background. This means issuing pop
operations to the rebuild and remembering how far we are in the rebuild
so that we can issue pop-maxes if needed. Assuming that we do multiple
pushes on the rebuild with every operation, we’ll finish before pops and
pop-maxes can decrease the element count by more than a constant
fraction.
Open questions
I’m sure that there’s a cleaner way to accomplish all this. What is it?