skip to content

questions

12

In a rolling-maximum deque over the last k readings, why can an older smaller reading be dropped forever?

level: juniorimportance: must knowfreq 62%

answer

  1. ask who can still win later
  2. windows only move forward
  3. compare the newest reading with older ones
  4. an older, smaller reading is shadowed
  5. survivors form a decreasing run

basics

~20 s

A reading older and smaller than the newest one can never win again: every remaining window that contains it also contains that newer, larger reading. Being dominated, it is evicted from the back and never revisited.

solid answer

~50 s

The deque holds only readings that could still be the answer for some future window. When reading `i` arrives, any candidate already held whose value is `<=` the new reading is dominated: it sits at an earlier position, so every window that still includes it also includes `i`, and the new reading is at least as large. There is no future window in which the older one wins, so it is popped off the back before the new index is appended. What survives is a strictly decreasing run of candidate values, oldest at the front, and that front candidate is the maximum of the current window once out-of-range positions have been dropped from the front. The whole trick rests on windows moving only forward — dominance is permanent, which is what makes throwing readings away safe instead of reckless.

go deeper

for a junior

Be ready to say in one breath why a reading that is older and smaller than the newest one can never win a later window, and where the answer for the current window sits.

for a middle

Explain the invariant precisely: positions increase towards the back while values decrease, and the eviction loop restores that before each append. Say what it buys and what it costs.

for a senior

Defend the invariant against real data — repeated readings, long flat stretches, windows wider than the burst — and explain why discarding readings is safe here rather than a risk to be hedged.

for a principal

Own the boundary: dominance holds only while queries advance monotonically and only for a single winner. Name that limit before a requirement for arbitrary ranges or a top-three list quietly crosses it.

## The problem this shape of structure solves A collector receives load readings one per tick, and a dashboard must show the peak load over the last `k` ticks after every reading. The obvious implementation rescans the last `k` readings at every step, costing `O(n*k)` over a stream of `n` readings. A monotonic deque replaces that rescan by keeping a shortlist of readings that are still *candidates* to be the answer of some window that has not been reported yet. ## What makes a reading a candidate Windows advance by exactly one position each step, and they never move backwards. Fix two positions `p < q` inside the current window with `reading[p] <= reading[q]`. Any window that still contains `p` starts at or before `p` and has length `k`, so it ends at or after `p`; since `q > p` and `q` is inside the current window, every future window containing `p` also contains `q`. The maximum of such a window is therefore at least `reading[q]`, which is at least `reading[p]`. So `p` can never *be* the reported maximum on its own merits again. It is **dominated**, and dominance is permanent because windows only move forward. That single observation is the whole structure. When reading `i` arrives, walk from the back of the deque popping every candidate whose value is `<=` the new reading — each of those is dominated by `i` — then append `i`. Nothing that could still win is ever discarded, so correctness is preserved; everything that cannot win is discarded, so the shortlist stays small. ## The invariant that results After each append, the deque holds positions in increasing order (they are appended in arrival order) whose readings are strictly decreasing from front to back. Two consequences follow directly: - The **front** holds the largest surviving candidate, so it is the answer for the current window once expired positions have been removed from the front. - Every other candidate is a *runner-up in waiting*: smaller than the front, but newer, so it inherits the title once the front ages out of the window. That second point is what people miss. A reading smaller than everything currently held is **not** useless and must not be skipped: it is appended at the back precisely because the larger candidates ahead of it will expire before it does. ## A trace Readings `9, 3, 2, 1, 7` with window length `k = 3`, deque shown as positions: | step | reading | evictions | deque | reported | |---|---|---|---|---| | 0 | 9 | none | [0] | — | | 1 | 3 | none (9 > 3) | [0,1] | — | | 2 | 2 | none (3 > 2) | [0,1,2] | 9 | | 3 | 1 | none; position 0 expires | [1,2,3] | 3 | | 4 | 7 | drop 3, 2, 1 (all `<= 7`) | [4] | 7 | Step 4 shows dominance doing its job: one large reading wipes out three smaller, older candidates at once. Step 3 shows the other half of the structure — the front leaving because its *position* has aged out of the window, not because a larger reading arrived. ## Where the argument stops Dominance holds only because queries advance monotonically. If the product later needed the maximum over an arbitrary range chosen after the fact, a discarded reading could be the only one in range and dropping it would be wrong; that requires a different structure entirely. Likewise, dominance is about a single winner: it says nothing about the second-largest reading, so a request for the top three in each window is not a small edit to this structure. ## Common wrong answers "You must keep the whole window so you can recompute" — no, only the decreasing run can ever win. "The deque is a sorted copy of the window" — it is a *subset*, and it is ordered by position, with values happening to decrease as a consequence of eviction. "A smaller new reading is useless" — it is the heir apparent. "The maximum could be anywhere in the deque" — it is always at the front, which is exactly what makes the answer `O(1)` to read.

  • Where does the current window's maximum sit in the deque, and why there?
    At the front. Candidates are held oldest-first with values decreasing towards the back, so once aged-out positions are removed from the front, the front candidate is both in-window and the largest survivor. Anything that beat it was itself evicted as dominated earlier.
  • What happens when the newest reading is smaller than every candidate currently held?
    Nothing is evicted and the new position is appended at the back as the smallest candidate. It matters later: once the larger, older candidates age out of the window, this reading may become the window maximum, so discarding it would be wrong.
  • Would the same dominance argument survive if queries could reach backwards as well as forwards?
    No. Dominance relies on a newer position outliving every older one, which is only true because windows advance. If a query could cover a range ending before the newer reading, the older one might be the only candidate in range, and evicting it would produce a wrong answer.

Think of contenders for a title where every bout is scheduled later than the last. The moment a stronger, younger contender shows up, the older weaker one can never hold the belt again — so you can strike them off the list rather than carry them.

saying these in an interview costs you the question

  • Says the deque must hold every reading in the window
  • Describes the deque as a sorted copy of the window
  • Evicts the newer reading and keeps the older larger one
  • Skips a new reading because it is smaller than the current peak
  • Thinks the maximum can sit anywhere inside the deque

context

open as a page

In a next-greater-element scan over an array, what do the indices on the stack represent?

level: juniorimportance: must knowfreq 65%

basics

~10 s

The stack holds indices of items still waiting for a greater value to their right, kept in decreasing order of value. Each new item answers every stacked index it beats, then is pushed itself.

open as a page

A rolling-maximum deque has a nested eviction loop, so why is the whole scan O(n)?

level: middleimportance: must knowfreq 68%

basics

~20 s

Each position is appended to the candidate list exactly once and removed at most once, so the inner eviction loop performs at most n removals across the entire scan. Total work is linear even though a single step can evict many candidates.

open as a page

Scanning building heights with a monotonic stack, why is the widest rectangle capped by a bar computed only when that bar is popped?

level: middleimportance: must knowfreq 70%

basics

~20 s

A rectangle needs both edges. The right edge stays unknown until a shorter bar arrives, and that arrival is exactly what triggers the pop; the left edge is the bar left underneath on the stack. Only at pop time are both known.

open as a page

Why is a monotonic-stack next-greater scan O(n) when a while loop sits inside the for loop?

level: middleimportance: must knowfreq 78%

basics

~20 s

Every index is pushed exactly once and popped at most once, so the inner while loop runs at most n times in total across the scan. Total work is linear even though one step may pop many indices.

open as a page

In a daily-score series, why does a monotonic stack give each day's weaker-prior-day streak as an index gap rather than a pop count?

level: juniorimportance: should knowfreq 45%

basics

~20 s

Each popped day already absorbed the days it dominated, so counting pops undercounts. The streak is today's index minus the index still sitting on top of the stack, which spans every absorbed day in one subtraction.

open as a page

Why does a rolling-maximum deque store reading positions rather than the reading values?

level: middleimportance: should knowfreq 55%

basics

~20 s

Positions do double duty: the value at a position decides dominance, while the position itself decides expiry. Storing bare values leaves no exact way to tell when a candidate has aged out of the window, and repeated readings then produce wrong answers.

open as a page

Why can a monotonic-stack scan over strictly increasing bar heights return 0 unless a sentinel bar is appended?

level: middleimportance: should knowfreq 50%

basics

~20 s

Rectangles are settled only when a bar is popped, and a strictly rising skyline never triggers a pop. The loop ends with every bar still stacked and nothing measured, so the running best keeps its initial value.

open as a page

In a next-smaller-element stack scan over a price list with ties, which pop comparison do you use?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Pop while the stacked price is strictly greater than the current one for the next strictly cheaper supplier; pop while greater or equal for the next cheaper-or-equal one. Ties are where the two diverge, so the specification decides.

open as a page

Deque, lazy-deletion heap, or per-window recompute: how do you pick a rolling-maximum implementation?

level: principalimportance: should knowfreq 38%

basics

~20 s

Pick by window size, stream volume and maintenance cost, not by asymptotics alone. Per-window recompute is fine when the window is tiny, a lazy-deletion heap is simple but grows with the backlog, and the deque's linear total with window-bounded memory pays off at high volume.

open as a page

How does a monotonic-stack next-greater scan cover a circular array without duplicating it in memory?

level: middleimportance: nice to knowfreq 34%

basics

~20 s

Run the loop for 2n steps and index the data with step modulo n, pushing only during the first n steps. The second lap lets early positions see values that wrap past the end; whatever is still stacked has no answer.

open as a page

How does a row-by-row column-height reduction turn a binary seat grid into one histogram scan per row?

level: seniorimportance: nice to knowfreq 32%

basics

~20 s

Sweep rows top to bottom keeping, per column, the count of consecutive free cells ending at the current row; an occupied cell resets that column to zero. Each updated row is a histogram, scanned for its widest block.

open as a page