skip to content

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

level: seniorimportance: should knowfreq 45%

answer

  1. direction and ties are separate decisions
  2. what should an equal price do
  3. one operator selects the semantics
  4. strict leaves equals sitting together
  5. non-strict pops the equal neighbour

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.

solid answer

~50 s

Going from next-greater to next-smaller flips exactly one thing — the comparison in the pop test — and the stack's monotonic direction flips with it, from non-increasing to non-decreasing. The tie decision is separate and easy to get silently wrong. With a strict test (`pop while price[top] > current`), equal prices are never popped by each other, so a whole run of identical prices stays stacked and all of them resolve to the same later, strictly cheaper supplier. With a weak test (`pop while price[top] >= current`), each price in the run is popped by its equal successor, so every member answers to the one immediately after it. Both variants stay `O(n)` — the accounting does not care — so nothing crashes and nothing gets slow: the failure is a plausible-looking wrong answer on duplicate-heavy data. Ask what an equal price means to the consumer before choosing.

go deeper

for a junior

Recall that the pop test is where the direction lives, and that changing it between strict and non-strict changes what happens when two values are equal.

for a middle

Explain, with a short run of equal values, exactly which position each member ends up pointing to under each comparison, and state that both stay linear.

for a senior

Demonstrate the production instinct: this is a silent, plausible wrong answer that only appears on duplicate-heavy rows, so pin the intended semantics with a duplicates fixture and name the rule in the code.

for a principal

Own turning the ambiguity into a decision: get the consumer to state what an equal price means before anyone writes the comparison, and make one comparator-driven implementation serve all four variants rather than four copies drifting apart.

## Two independent decisions, often conflated Adapting the next-greater scan to a procurement price list, where each entry is a supplier quote and you want the nearest cheaper quote further down the list, involves two decisions that people tend to merge into one: 1. **Direction.** Next greater or next smaller. This flips only the comparison operator in the pop test, from "top is less than current" to "top is greater than current". Consequently the invariant flips too: the stacked values become non-decreasing from bottom to top instead of non-increasing. Nothing else changes — same pushes, same result array, same bound. 2. **Tie handling.** Whether an *equal* value counts as an answer. This is the choice between a strict (`>`) and a weak (`>=`) pop test, and it is a specification question, not an implementation detail. A candidate who flips direction confidently but never mentions ties has answered half the question. ## What each tie rule actually produces Take quotes 40, 30, 30, 30, 20 and ask each index for its next cheaper quote to the right. **Strict, `pop while price[top] > current`.** Arriving at the second 30, the top holds 30; `30 > 30` is false, so nothing pops and the new index is pushed on top. The run accumulates. When 20 arrives, all three 30s and then the 40 pop, and every one of them is answered with the position of the 20. Semantics: *next strictly cheaper*. The stack is non-decreasing bottom to top, with ties allowed to sit side by side. **Weak, `pop while price[top] >= current`.** Arriving at the second 30 pops the first 30 and answers it with the second's position. Each member of the run answers to its immediate equal successor; only the last 30 in the run waits for the 20. Semantics: *next cheaper or equal*. The stack is strictly increasing bottom to top, since equals never coexist. Both results are internally consistent. Which is *correct* depends on whether "a supplier at the same price" satisfies the business question — for "find me a cheaper option" it does not; for "find me the next quote that is not more expensive" it does. ## Why this bug is expensive in production Three properties make it nasty. First, it is invisible on distinct data: any price list with no repeats produces identical output under both rules, so a test fixture with unique prices passes either way. Second, it does not degrade performance — the push-once, pop-at-most-once accounting holds under both comparisons, so the scan stays linear and no timeout fires. Third, the wrong answer is *plausible*: it points at a real supplier at a real position, just the wrong one, and it is wrong only on the rows where prices repeat, which in real catalogues is exactly the crowded, competitive part of the data. The failure surfaces as a slow-burning data-quality complaint, not a crash. The defence is a test fixture with deliberate duplicate runs — several equal prices in a row, a run at the start, a run at the very end — and an assertion on which occurrence each member points to. That fixture is what separates candidates who have shipped this from candidates who have solved it on a whiteboard. ## The four variants, stated once With a left-to-right scan and the stack holding open questions: | Wanted | Pop while | Stack, bottom to top | | --- | --- | --- | | next strictly greater | `top < current` | non-increasing | | next greater or equal | `top <= current` | strictly decreasing | | next strictly smaller | `top > current` | non-decreasing | | next smaller or equal | `top >= current` | strictly increasing | One operator selects the row. Memorising the table is less useful than deriving it: ask "should an equal value close this question?" — if yes, include equality in the pop test; if no, exclude it and let equals accumulate. ## Edge cases worth naming A run of equal prices at the *end* of the list survives to the end under either rule, minus whichever members the weak rule answered internally. An all-equal list produces no answers at all under the strict rule and answers everything except the last element under the weak rule — a good sanity check to state out loud, because it separates the two rules maximally. And if the consumer wants *distance* to the next cheaper quote rather than its position, the tie rule changes those distances dramatically inside a run: all-the-way-to-the-end under strict, one step under weak. ## What to say when asked "Direction is one operator: pop while the stacked price is greater than the current one. Ties are a separate decision — strict comparison means equal prices never answer each other and a whole run resolves to the same later cheaper quote; non-strict means each answers its immediate equal neighbour. Both are linear, so the only symptom of picking wrong is quietly wrong data on duplicate-heavy input, which is why I would pin it with a duplicates fixture."

  • Going from next-greater to next-smaller, what exactly changes in the algorithm?
    Only the comparison in the pop test, from the stacked value being less than the current to being greater than it. The invariant flips along with it: the stack reads non-decreasing bottom to top instead of non-increasing. Pushes, the result array, the leftovers rule and the linear bound are all unchanged, which is why one implementation with an injected comparator covers all four variants.
  • Does using the non-strict comparison affect the running time?
    No. Each index is still pushed exactly once and popped at most once, so the total inner-loop work is bounded by the number of pushes either way and the scan stays linear. The non-strict rule tends to keep the stack shallower on duplicate-heavy data, which can help constants and memory, but the asymptotic class is identical.
  • How would you catch this bug before it reaches production?
    With a fixture built entirely of duplicates: a run in the middle, a run at the start, a run at the end, and an all-equal list. Assert the exact position each member points to, not just that an answer exists. Unique-value fixtures cannot catch it because both comparisons agree on distinct data, which is why the defect survives most test suites.

saying these in an interview costs you the question

  • Treats the tie rule as an implementation detail rather than a specification
  • Claims duplicates make the scan quadratic
  • Flips the direction but leaves the equality behaviour unexamined
  • Says both comparisons give the same answers on any input
  • Tests only with distinct values and declares it correct

context