Stacks & Queues · 22 of 38
Stack Permutations (Check if an array is stack permutation of other)
Train cars enter a side track one by one; check if they can leave in a given order.
- Simple way
- time O(4^n)
room O(n) - Clever way
- time O(n)
room O(n)
The problem, simply
You get two arrays of the same distinct numbers, A and B. Numbers from A arrive in order. You have one stack. At any moment you can push the next number of A onto the stack, or pop the top of the stack into the output. Can the output be exactly B?
If yes, B is a stack permutation of A. Return true or false.
A story to remember it
Picture it
A stuck case: A = 1, 2, 3 and B = 3, 1, 2. To let 3 leave first, 1 and 2 must wait on the stack with 2 above 1. B then wants 1, but 2 is in the way.
- done
- answer
- rejected
Examples
Swap the first two
- In
A = [1, 2, 3], B = [2, 1, 3]- Out
true
Push 1, push 2, pop 2, pop 1, push 3, pop 3.
Big, small, middle is stuck
- In
A = [1, 2, 3], B = [3, 1, 2]- Out
false
For 3 to leave first, 1 and 2 wait with 2 on top. Then 1 cannot leave before 2.
Fully reversed
- In
A = [1, 2, 3], B = [3, 2, 1]- Out
true
Push everything, then pop everything.
A longer order
- In
A = [1, 2, 3, 4, 5], B = [2, 1, 4, 5, 3]- Out
true
This is the run in the animation. 3 waits at the bottom while 4 and 5 come and go.
One car
- In
A = [7], B = [7]- Out
true
Push 7, pop 7. Arrays of different lengths are always false.
Way 1: the simple way
Try every possible plan. At each moment there are at most two choices: push the next car, or pop the top car. Explore both with backtracking, and when all cars have left, compare the output with B.
Every choice for A = 1, 2: at each step either push the next car or pop the top. Only two orders can come out: 2, 1 and 1, 2.
- answer
import java.util.*;
class Solution {
public boolean isStackPermutation(int[] a, int[] b) {
if (a.length != b.length) return false;
return tryAll(a, b, 0, new ArrayDeque<>(), new ArrayList<>());
}
private boolean tryAll(int[] a, int[] b, int next, Deque<Integer> stack, List<Integer> out) {
if (next == a.length && stack.isEmpty()) { // every car has left
for (int k = 0; k < b.length; k++) if (out.get(k) != b[k]) return false;
return true;
}
if (next < a.length) { // choice 1: push the next car
stack.push(a[next]);
if (tryAll(a, b, next + 1, stack, out)) return true;
stack.pop();
}
if (!stack.isEmpty()) { // choice 2: pop the top car
int x = stack.pop();
out.add(x);
if (tryAll(a, b, next, stack, out)) return true;
out.remove(out.size() - 1);
stack.push(x);
}
return false;
}
}How long it takes: a plan is a string of n pushes and n pops, and there are up to about 4^n such strings to explore, O(4^n). Extra room: the stack, the output and the recursion are each O(n).
Way 2: the clever way
There is never a real choice. If the top of the stack is the next car B wants, popping it now is the only way it can ever leave in time. If it is not, the only thing you can do is push more.
- Keep
j, the position inBof the next car that must leave. - For each car
xofA: pushx. - While the stack is not empty and its top equals
B[j]: pop and movejforward. - At the end,
Bis reachable exactly when the stack is empty.
Push in order, pop whenever the top is the next car B wants
01 / 12
Cars come in the order 1, 2, 3, 4, 5. They must leave in the order 2, 1, 4, 5, 3. The stack is a side track where cars wait.
- checking
- done
- answer
- just added
import java.util.*;
class Solution {
public boolean isStackPermutation(int[] a, int[] b) {
if (a.length != b.length) return false;
Deque<Integer> stack = new ArrayDeque<>();
int j = 0; // next car that must leave
for (int x : a) {
stack.push(x); // the car rolls into the siding
while (!stack.isEmpty() && stack.peek() == b[j]) {
stack.pop(); // it is wanted now, send it out
j++;
}
}
return stack.isEmpty(); // every car left in B's order
}
}How long it takes: each car is pushed once and popped at most once, O(n). Extra room: the stack can hold all n cars, O(n).
Watch out for
- Popping only once per push. After a pop, the new top might also be wanted. Use a
while, not anif. - Returning
truewhenjreaches the end without checking lengths. Checka.length == b.lengthfirst. - Repeated numbers. The greedy rule assumes distinct cars. With repeats, "the top equals the wanted value" may be true for the wrong copy, and the problem needs more care.
What interviewers ask next
- How many orders can come out of a stack for
ncars? The Catalan numberC(n) = (2n)! / ((n + 1)! n!): 1, 2, 5, 14, 42, ... Quant interviewers love this link. - Which orders are impossible? Exactly those where some three cars leave in the shape big, small, middle, like
3, 1, 2. In grown-up words, the order contains the pattern 312. - Validate push and pop sequences (LeetCode 946) is the same problem with
pushedandpoppedarrays. - Can two stacks in a row produce every order? No, but they produce many more. With a queue instead of a stack, only the same order
Acan come out.
Remember this
Grown-up words: the siding is a stack, a plan of pushes and pops is an operation sequence, B being reachable means it is a stack permutation, and popping as soon as possible is a greedy simulation.