065. Validate Stack Push/Pop Sequences
Problem
Given pushed with distinct values and popped of the same length, decide whether one stack can produce popped while pushing values in pushed order. At any time you may push the next input or pop the current top. Empty sequences are valid.
Examples
Example 1
valid_stack_sequences([1,2,3,4,5], [4,5,3,2,1])
=> TrueThe required pop chain can be simulated.
Example 2
valid_stack_sequences([1,2,3,4,5], [4,3,5,1,2])
=> FalseTwo blocks access to one near the end.
Example 3
valid_stack_sequences([], [])
=> TrueNo operations are required.
Reveal the worked solution and reasoning
Why this mechanism works
Whenever the current top equals the next required output, popping it immediately cannot hurt: leaving it would only block access to values beneath it, and pushing more values is unnecessary for producing that required output. Simulate this forced opportunity after every push.
State and boundaries
j is the next required pop index. stack contains values pushed but not yet matched to the output sequence.
Protocol
- Push the next input value.
- While top equals popped[j], pop and advance j.
- Continue with the next push.
- Accept only if every requested output was matched.
Visualization
push 1,2,3,4; need 4 -> pop
next need 5 -> push 5, pop
then 3,2,1 can unwindPython implementation
def valid_stack_sequences(pushed, popped):
if len(pushed) != len(popped):
return False
stack = []
j = 0
for value in pushed:
stack.append(value)
while stack and j < len(popped) and stack[-1] == popped[j]:
stack.pop()
j += 1
return j == len(popped)
Worked execution
valid_stack_sequences([1,2,3,4,5], [4,5,3,2,1])
L2 valid_stack_sequences: if len(pushed) != len(popped): pushed=[1, 2, 3, 4, 5]; popped=[4, 5, 3, 2, 1] L4 valid_stack_sequences: stack = [] pushed=[1, 2, 3, 4, 5]; popped=[4, 5, 3, 2, 1]; stack=[] L5 valid_stack_sequences: j = 0 pushed=[1, 2, 3, 4, 5]; popped=[4, 5, 3, 2, 1]; stack=[]; j=0 L6 valid_stack_sequences: for value in pushed: pushed=[1, 2, 3, 4, 5]; popped=[4, 5, 3, 2, 1]; stack=[]; j=0; value=1 L7 valid_stack_sequences: stack.append(value) pushed=[1, 2, 3, 4, 5]; popped=[4, 5, 3, 2, 1]; stack=[1]; j=0; value=1 L6 valid_stack_sequences: for value in pushed: pushed=[1, 2, 3, 4, 5]; popped=[4, 5, 3, 2, 1]; stack=[1]; j=0; value=2 L7 valid_stack_sequences: stack.append(value) pushed=[1, 2, 3, 4, 5]; popped=[4, 5, 3, 2, 1]; stack=[1, 2]; j=0; value=2 L6 valid_stack_sequences: for value in pushed: pushed=[1, 2, 3, 4, 5]; popped=[4, 5, 3, 2, 1]; stack=[1, 2]; j=0; value=3 ... 11 intermediate state changes omitted ... L9 valid_stack_sequences: stack.pop() pushed=[1, 2, 3, 4, 5]; popped=[4, 5, 3, 2, 1]; stack=[1]; j=3; value=5 L10 valid_stack_sequences: j += 1 pushed=[1, 2, 3, 4, 5]; popped=[4, 5, 3, 2, 1]; stack=[1]; j=4; value=5 L9 valid_stack_sequences: stack.pop() pushed=[1, 2, 3, 4, 5]; popped=[4, 5, 3, 2, 1]; stack=[]; j=4; value=5 L10 valid_stack_sequences: j += 1 pushed=[1, 2, 3, 4, 5]; popped=[4, 5, 3, 2, 1]; stack=[]; j=5; value=5
Result: True. The required pop chain can be simulated.
valid_stack_sequences([1,2,3,4,5], [4,3,5,1,2])
L2 valid_stack_sequences: if len(pushed) != len(popped): pushed=[1, 2, 3, 4, 5]; popped=[4, 3, 5, 1, 2] L4 valid_stack_sequences: stack = [] pushed=[1, 2, 3, 4, 5]; popped=[4, 3, 5, 1, 2]; stack=[] L5 valid_stack_sequences: j = 0 pushed=[1, 2, 3, 4, 5]; popped=[4, 3, 5, 1, 2]; stack=[]; j=0 L6 valid_stack_sequences: for value in pushed: pushed=[1, 2, 3, 4, 5]; popped=[4, 3, 5, 1, 2]; stack=[]; j=0; value=1 L7 valid_stack_sequences: stack.append(value) pushed=[1, 2, 3, 4, 5]; popped=[4, 3, 5, 1, 2]; stack=[1]; j=0; value=1 L6 valid_stack_sequences: for value in pushed: pushed=[1, 2, 3, 4, 5]; popped=[4, 3, 5, 1, 2]; stack=[1]; j=0; value=2 L7 valid_stack_sequences: stack.append(value) pushed=[1, 2, 3, 4, 5]; popped=[4, 3, 5, 1, 2]; stack=[1, 2]; j=0; value=2 L6 valid_stack_sequences: for value in pushed: pushed=[1, 2, 3, 4, 5]; popped=[4, 3, 5, 1, 2]; stack=[1, 2]; j=0; value=3 ... 7 intermediate state changes omitted ... L6 valid_stack_sequences: for value in pushed: pushed=[1, 2, 3, 4, 5]; popped=[4, 3, 5, 1, 2]; stack=[1, 2]; j=2; value=5 L7 valid_stack_sequences: stack.append(value) pushed=[1, 2, 3, 4, 5]; popped=[4, 3, 5, 1, 2]; stack=[1, 2, 5]; j=2; value=5 L9 valid_stack_sequences: stack.pop() pushed=[1, 2, 3, 4, 5]; popped=[4, 3, 5, 1, 2]; stack=[1, 2]; j=2; value=5 L10 valid_stack_sequences: j += 1 pushed=[1, 2, 3, 4, 5]; popped=[4, 3, 5, 1, 2]; stack=[1, 2]; j=3; value=5
Result: False. Two blocks access to one near the end.
Why the solution is correct
Every simulated operation is legal. The next requested value cannot be popped unless it is on top; when it is, taking it cannot invalidate a later operation. Thus this greedy simulation succeeds exactly when the required order is achievable.
Time and space
O(n) time and O(n) space; each value is pushed and popped at most once.
Common mistakes
A single pop check can miss a whole newly exposed chain. Comparing unordered value sets does not validate stack order.
Reasoning card
State: j is the next required pop index. stack contains values pushed but not yet matched to the output sequence.
Invariant: The stack models exactly the values currently present.
Decision to rehearse: Push each input and pop greedily while the next requested value is on top.
Finish: Accept only if every requested output was matched
Transfer exercise
Return one valid push/pop operation sequence as a witness, rather than only a Boolean result.