Stacks & Queues · 13 of 38
Implement a method to insert an element at its bottom without using any other data structure
Slide a new block under a tower of blocks, using only your hands and your memory, with no second table to put blocks on.
- Simple way
- time O(n)
room O(n) helper stack - Clever way
- time O(n)
room O(n) call stack
The problem, simply
You get a stack and a value x. Put x at the very bottom of the stack. Everything else must stay in the same order above it.
The catch: you may not use any other data structure, no second stack, no array, no list. You may only use the stack's own push, pop, peek and isEmpty, and recursion.
A story to remember it
Picture it
Before and after. The stack 1, 2, 3, 4 (4 on top) gets 0 slid under it. The order of 1 to 4 does not change.
- just added
Examples
A normal stack
- In
stack = [1, 2, 3, 4] (4 on top), x = 0- Out
[0, 1, 2, 3, 4]
0 is under 1. Everything else is where it was.
One block
- In
stack = [5], x = 10- Out
[10, 5]
5 is popped, 10 goes down, 5 goes back on top.
Empty stack
- In
stack = [], x = 7- Out
[7]
The stack is already empty, so 7 is pushed right away. This is also the base case of the recursion.
Same values
- In
stack = [2, 2], x = 2- Out
[2, 2, 2]
Nothing special happens with repeats. The method never compares values.
Way 1: the simple way
If the rules allowed a second stack, you would pour everything into it, push x, and pour it all back.
- Pop every item from the stack into a helper stack. The helper now holds them upside down.
- Push
xonto the empty stack. - Pop every item from the helper back onto the stack. They land in the original order.
Way 1 halfway: everything is in the helper (upside down), and x = 0 is on the empty stack. Pouring the helper back gives 0, 1, 2, 3, 4.
- just added
class Solution {
public void insertAtBottom(Deque<Integer> stack, int x) {
Deque<Integer> helper = new ArrayDeque<>(); // not allowed by the rules
while (!stack.isEmpty()) helper.push(stack.pop());
stack.push(x);
while (!helper.isEmpty()) stack.push(helper.pop());
}
}How long it takes: every item is moved twice, O(n). Extra room: a helper stack of n items, O(n). It works, but it breaks the rule.
Way 2: the clever way
Let recursion be the helper. Each call holds one item in a local variable, like a friend holding one block. The waiting calls form a hidden stack, the call stack.
- If the stack is empty, push
xand return. This is the floor. - Otherwise pop the top item into a local variable
top. - Call
insertAtBottom(stack, x)on the smaller stack. - When that call returns, push
topback.
Each call holds one block until the bottom is reached
01 / 09
The stack is 1, 2, 3, 4 with 4 on top. We want 0 at the bottom. No calls are waiting yet.
- x
- 0
- looking now
- answer
- just added
class Solution {
public void insertAtBottom(Deque<Integer> stack, int x) {
if (stack.isEmpty()) { // the floor: x goes here
stack.push(x);
return;
}
int top = stack.pop(); // this call holds one block
insertAtBottom(stack, x); // the rest of the work
stack.push(top); // put the block back on the way out
}
}How long it takes: each item is popped once and pushed once, O(n). Extra room: n + 1 calls wait on the call stack, O(n). The rule only bans data structures you build yourself; the call stack is how the language runs functions.
Watch out for
- Pushing
topback before the recursive call puts it underx, which breaks the order. Recurse first, push after. - A very tall stack can overflow the call stack. In Java that is a
StackOverflowErrorat a depth of a few thousand to tens of thousands, depending on settings. - The method changes the stack in place. It does not need to return anything.
What interviewers ask next
- Reverse a stack using only recursion. Pop the top, reverse the rest, then insert the popped item at the bottom with this method. That is
O(n^2)time. - Sort a stack using only recursion. Pop the top, sort the rest, then insert the item in its sorted place with a method shaped like this one, stopping when the top is not bigger.
- Insert at position
kfrom the bottom. Same idea, but stop popping when the stack size iskinstead of zero. - Can you do it without recursion and without extra storage? Not with only push and pop. Something has to remember the popped items, and the call stack is that something.
- Is the space really "no extra data structure"? It is
O(n)hidden memory. Saying that clearly is part of a good answer.
Remember this
Grown-up words: the friends holding blocks are recursive calls on the call stack, the floor is the base case, and putting blocks back on the way out is the unwinding of the recursion.