Skip to content
dsa450for five year olds

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

arrayinput A
012123
arraytarget B
012312j
stackstack
12top

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.

treechoices
startpush 1push 2pop 2pop 1pop 1push 2pop 2out: 2, 1out: 1, 2

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
java
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.

  1. Keep j, the position in B of the next car that must leave.
  2. For each car x of A: push x.
  3. While the stack is not empty and its top equals B[j]: pop and move j forward.
  4. At the end, B is reachable exactly when the stack is empty.

Push in order, pop whenever the top is the next car B wants

01 / 12

arrayinput A (push in this order)
0123412345i
arraytarget B (must pop in this order)
0123421453j
stackstack (the siding)
empty

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
java
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 an if.
  • Returning true when j reaches the end without checking lengths. Check a.length == b.length first.
  • 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 n cars? The Catalan number C(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 pushed and popped arrays.
  • 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 A can 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.