Run it
Both versions of the comparison, on the sequence that separates them.
The min stack grows only when the minimum changes, which is the honest answer to how much extra space it costs.
And the pair variant: easier to get right, at a fixed cost per element.
Why a scan is not allowed
The obvious min() walks the stack, which is O(n) and is exactly what the question forbids. Caching a single minimum in a variable is the next attempt and it fails on pop: when the minimum is removed, there is nothing to fall back to, and finding the new one is another scan.
So the structure has to remember the minimum at every level. A parallel stack does that: entry k is the minimum of the first k pushes, so popping a level restores the previous answer for free.
The <= that everybody gets wrong first
Push 2, then 1, then 1. With < the min stack is [2, 1]; with <= it is [2, 1, 1]. Now pop once. Both versions remove a 1 from the data, and the strictly-less version also removed its only 1 from the min stack — so it reports 2 while a 1 is still in the stack.
The editor below runs both and prints the two answers side by side. It is the single most common bug in this question, and an interviewer who asks for duplicates is asking about exactly this line.
The one-stack variants
Two alternatives come up as follow-ups, and both are worth knowing.
Store pairs. Push (value, min_so_far) and the minimum is stack[-1][1]. Simpler to get right, and it costs one extra number per element rather than one per new minimum — worse in the best case, identical in the worst.
Store deltas. Keep one stack and push the difference from the current minimum, tracking the minimum in a variable. It is O(1) extra space, it is genuinely clever, and it is fiddly enough that mentioning it as an option scores better than attempting it under time pressure.
What the question is testing
Whether you can turn "report an aggregate cheaply" into "keep the aggregate as part of the structure". That is the transferable idea: the same move gives a queue with O(1) minimum (two deques, the monotonic one), a sliding-window maximum, and a stack that reports its sum. The interviewer usually follows with one of those.
The second thing being tested is whether you volunteer the duplicate case yourself. Saying "I will use <= so equal minimums each get an entry" before being asked is the answer they are listening for.
What to say out loud
A second stack holding the minimum at each level. Push copies the value onto the min stack when it is less than or equal to the current minimum; pop removes it from the min stack if it matches. min() is the top of that stack, so it is O(1). The trap is using strictly less than, which loses a duplicate minimum on the first pop.
Edge cases to raise
Volunteering these is most of what separates a correct answer from a good one.
min() on an empty stack. Decide whether it raises or returns a sentinel, and say which before writing it - an IndexError from self.mins[-1] is a reasonable answer if it is deliberate.
Space is proportional to how often the minimum changes, not to n. An ascending sequence leaves the min stack at length 1; a descending one makes it as long as the data. Worth stating, because it is the honest answer to "how much extra space?"
The two stacks must stay in step. Any method that touches data without considering mins - a clear(), a bulk load, a __setitem__ - is where this class breaks later.
The follow-ups interviewers ask
"Now make it report the maximum too." One more parallel stack with the comparison mirrored to >=. Worth saying out loud that the duplicate case flips with it - the same <= trap appears as >=, and getting one right and the other wrong is a common slip.
"Can you do it in O(1) extra space?" Yes, by storing the difference from the current minimum rather than the value, and keeping the minimum in a variable. When you pop a value that is the minimum, the stored delta tells you what the previous minimum was. It is genuinely clever and fiddly under time pressure - naming it and then writing the two-stack version is the better answer.
"What about a queue with O(1) minimum?" Harder, and the answer is a monotonic deque rather than a second stack: keep candidates in increasing order and discard from the back anything a new arrival makes irrelevant. That is the same structure as the monotonic stack and as sliding-window maximum.
Common wrong answers
"Keep the minimum in a variable and update it on push." Correct until the first pop. When the minimum leaves, there is nothing to fall back to and finding the new one is an O(n) scan.
"Sort the stack, or keep it sorted." A sorted stack is not a stack any more - push stops being O(1), and pop returns the wrong element.
"Use min(self.data); it is a builtin so it is fast." Builtin and still O(n). The question asks for O(1), and "it is implemented in C" does not change the complexity.
Recap in one screen
- The min stack holds one entry per level at which the minimum changed, not one per element.
- Pushing 1 twice pushes 1 onto the min stack twice - that is what <= buys.
- After the pop, the minimum is still 1, because the second copy is underneath.
- Worth trying: Change <= back to < in MinStack and run the ascending sequence (1, 2, 3). Both versions agree there, which is why the bug survives a casual test.
- Worth trying: Add a max() as well. One more parallel stack, and the same >= question arrives in mirror image.