Topics › Recursion and Backtracking › Get a Strong Hold
Reverse a Stack
Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given a stack of integers. Reverse it in place: the element that was on top must end up at the bottom and vice versa. The intended solution uses recursion only, with no auxiliary array, list or second stack; the function call stack is the only extra memory.
Constraints: up to a few thousand elements (bounded by recursion depth); values fit in an int.
The stack is a Deque<Integer> backed by ArrayDeque; push, pop and peek work on the top. The examples list elements from bottom to top.
Example 1
stack (bottom to top) = [1, 2, 3] // 3 is on top
answer = [3, 2, 1] // 1 is on top
Example 2
stack (bottom to top) = [3, 2, 1, 4, 5]
answer = [5, 4, 3, 2, 1]
Reversing is not the same as popping everything and printing it: a pop loop reads the elements in reverse order, but destroys the stack. We need the same stack object to hold the reversed contents afterwards.
Approach 1: Brute force, through a list
Intuition. A stack hands its elements back in reverse order of insertion. If we pop everything into a list, the list reads top to bottom. Pushing that list back in order puts the old top in first, so it lands at the bottom, which is exactly a reversal.
Algorithm.
- Pop every element into a list
popped(old top first). - For each value in
popped, in order, push it.
static void reverseViaList(Deque<Integer> stack) {
List<Integer> popped = new ArrayList<>();
while (!stack.isEmpty()) popped.add(stack.pop()); // popped = old top, ..., old bottom
for (int v : popped) stack.push(v); // the old top is pushed first and ends at the bottom
}
Complexity. Time O(n). Space O(n) for the list.
Approach 2: Three pours with two helper stacks
Intuition. Suppose only stack operations are allowed, but extra stacks are fine. Pouring a stack into another stack (pop from one, push onto the other until empty) reverses it, but the result lives in the wrong object. Pouring it back restores the original order. So one pour is too few and two pours cancel out; three pours leave the original object holding the reversed sequence.
Algorithm.
- Pour
stackintofirst:firstis reversed. - Pour
firstintosecond:secondhas the original order. - Pour
secondback intostack:stackis reversed.
static void reverseWithTwoStacks(Deque<Integer> stack) {
Deque<Integer> first = new ArrayDeque<>();
Deque<Integer> second = new ArrayDeque<>();
while (!stack.isEmpty()) first.push(stack.pop()); // first holds the reversed order
while (!first.isEmpty()) second.push(first.pop()); // second holds the original order again
while (!second.isEmpty()) stack.push(second.pop()); // back into the original stack, reversed
}
Complexity. Time O(n), three passes. Space O(n) for the two helper stacks.
This is how you would do it with a stack API that gives no access to the underlying storage. It still needs auxiliary data structures, which the recursive version eliminates.
Approach 3: Optimal, recursion only
Intuition. A recursive call can hold one element in a local variable while the rest of the stack is processed, so the frames of the call stack act as the hidden helper storage. The reversal of a stack is: take the top off, reverse the remaining n - 1 elements, then put the element you took at the bottom. Putting something at the bottom is itself recursive: pop the top, insert the value at the bottom of what remains, push the popped top back.
Algorithm.
reverseRecursive(stack):
- If the stack is empty, return.
top = pop().reverseRecursive(stack).insertAtBottom(stack, top).
insertAtBottom(stack, v):
- If the stack is empty, push
vand return. top = pop().insertAtBottom(stack, v).push(top).
static void reverseRecursive(Deque<Integer> stack) {
if (stack.isEmpty()) return; // base case: the empty stack is its own reverse
int top = stack.pop();
reverseRecursive(stack); // the other n - 1 elements, reversed
insertAtBottom(stack, top); // the old top becomes the new bottom
}
static void insertAtBottom(Deque<Integer> stack, int v) {
if (stack.isEmpty()) { // base case: the bottom is right here
stack.push(v);
return;
}
int top = stack.pop(); // lift the elements above the bottom, one frame each
insertAtBottom(stack, v);
stack.push(top); // and put them back in the same order
}
Dry run on [1, 2, 3] (bottom to top). The reverseRecursive frames pop 3, then 2, then 1, reaching an empty stack. They finish in the opposite order:
| frame finishing | inserts at bottom | stack before | stack after |
|---|---|---|---|
| the one holding 1 | 1 | [] |
[1] |
| the one holding 2 | 2 | [1] |
pop 1, push 2, push 1 → [2, 1] |
| the one holding 3 | 3 | [2, 1] |
pop 1, pop 2, push 3, push 2, push 1 → [3, 2, 1] |
The deepest frame holds the original bottom and is the first to insert, so the bottom becomes the new top; the outermost frame holds the original top and inserts it last, at the very bottom.
Complexity. Time O(n^2): inserting at the bottom of a stack with k elements pops and re-pushes all k of them, and we do this for k = 0, 1, ..., n - 1. Space O(n): at the deepest point there are n frames of reverseRecursive plus up to n frames of insertAtBottom.
Note the trade: compared with Approach 1 this is slower, but it uses no data structure beyond the call stack, which is the whole point of the exercise. The same two-function pattern (pop, recurse, re-insert with a second recursion) solves the sorting version of this problem too.
Edge cases and pitfalls
- Empty stack. The base case of
reverseRecursivereturns immediately and the stack stays empty. - Single element. One pop, a call on the empty stack, then
insertAtBottompushes the element back. Reversing one element changes nothing. - Equal elements.
[5, 5, 5]reverses to itself; the algorithm does not care about values at all. - Reversing twice. Applying the recursion twice returns the original order; the Java tests check this.
- Forgetting to push
topback. IninsertAtBottom, thepush(top)after the recursive call is what restores every element above the new bottom. Omitting it leaves the stack with one element. - Recursion depth. About
2nframes at the deepest point. A thousand elements is comfortable; a million is not, and then Approach 1 or 2 is the practical choice. - Confusing reversal with printing. A loop of
popcalls prints the stack top to bottom but empties it. The task requires the stack to still contain all elements, in the reversed order.
Related problems
- Sort a stack using recursion replaces
insertAtBottomwithinsertSortedand is otherwise the same recursion. - Sort a Stack is the SDE-sheet version of that sorting task.
- Reverse an array is the recursive reversal of a structure that allows random access, where swapping ends does the job in
O(n). - Implement Queue using Stack relies on the fact that pouring one stack into another reverses the order.