Topics › Stacks and Queues › Learning
Sort a Stack
Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given a stack of integers. Rearrange it in place so that it is sorted with the largest element on top: popping it repeatedly yields the values in non-increasing order. You may use the stack operations push, pop, peek and isEmpty. The classic version forbids loops over auxiliary arrays and expects either recursion or a second stack.
In Java the stack is a Deque<Integer> (an ArrayDeque used through push, pop and peek).
Constraints: 0 ≤ n ≤ 10^4; recursive solutions are additionally limited by the depth of the call stack. Values fit in an int, duplicates and negatives allowed.
Example 1
stack (bottom → top) = [11, 2, 32, 3, 41]
after sorting, popping gives 41, 32, 11, 3, 2
Example 2
stack (bottom → top) = [3, 2, 1] // 1 is on top
after sorting, popping gives 3, 2, 1 // 3 must now be on top
Approach 1: Brute force, copy out and sort
Intuition. If the restrictions are lifted, sorting a stack is just sorting a list. Pop everything into a list, sort it ascending, and push it back smallest first so the largest ends on top. This is the reference answer the other two approaches must match, and it is the fastest. It is “brute force” only in the sense that it ignores the exercise’s rules.
Algorithm.
- Pop every element into a list.
- Sort the list ascending.
- Push the elements in that order; the last pushed (the largest) ends on top.
static void bruteForce(Deque<Integer> st) {
List<Integer> items = new ArrayList<>();
while (!st.isEmpty()) items.add(st.pop());
Collections.sort(items);
for (int v : items) st.push(v); // smallest pushed first ends at the bottom
}
Complexity. Time O(n log n) for the sort. Space O(n) for the list.
Approach 2: Recursion (insertion sort on the call stack)
Intuition. Recursion gives us a free second stack: the call stack. Each pending call can hold one popped value in a local variable while the rest of the stack is processed. That is enough to run insertion sort:
- To sort a stack: pop the top, sort the remaining (smaller) stack recursively, then insert the popped value back in its correct place.
- To insert
xinto a stack that is already sorted with its largest element on top: if the stack is empty or its top is≤ x,xbelongs on top, so push it. Otherwise the top is bigger thanxand must stay above it: pop it, insertxinto what remains, and push the bigger value back.
Both functions are correct by induction on the stack size. insertSorted only ever removes elements larger than x and puts them back in the same order, so sortedness is preserved.
Algorithm.
sortRecursive(st): if empty, return. Poptop, callsortRecursive(st), callinsertSorted(st, top).insertSorted(st, x): ifstis empty orst.peek() ≤ x, pushxand return. Otherwise popbigger, callinsertSorted(st, x), pushbigger.
static void sortRecursive(Deque<Integer> st) {
if (st.isEmpty()) return;
int top = st.pop();
sortRecursive(st); // the remaining stack is now sorted
insertSorted(st, top);
}
static void insertSorted(Deque<Integer> st, int x) {
if (st.isEmpty() || st.peek() <= x) {
st.push(x);
return;
}
int bigger = st.pop(); // x belongs below this element
insertSorted(st, x);
st.push(bigger);
}
Complexity. Time O(n^2): inserting into a sorted stack of size i can unwind all i elements, and 1 + 2 + ... + n is quadratic. Space O(n) for the call stack: sortRecursive nests n deep, and an insertSorted chain can nest up to n more on top of it.
That recursion depth is the practical weakness. Each frame is small, but a stack of tens of thousands of elements can throw StackOverflowError with default JVM settings.
Approach 3: Iterative, with a temporary stack
Intuition. Replace the call stack with an explicit second stack tmp, kept sorted with its smallest element on top. Take elements from the input one at a time. Before placing x on tmp, move every element of tmp that is smaller than x back onto the input stack; now tmp’s top is ≥ x, so pushing x keeps it sorted. The elements we moved back are not lost: they will be popped and placed again later.
When the input is empty, tmp holds everything with the smallest on top. Pouring it back into the input reverses the order, which leaves the largest on top as required.
Algorithm.
- While the input stack is not empty: pop
x. - While
tmpis not empty andtmp.peek() < x, movetmp’s top back to the input. - Push
xontotmp. - Finally pop everything from
tmpand push it onto the input.
static void sortWithTempStack(Deque<Integer> st) {
Deque<Integer> tmp = new ArrayDeque<>(); // invariant: sorted, smallest element on top
while (!st.isEmpty()) {
int x = st.pop();
while (!tmp.isEmpty() && tmp.peek() < x) st.push(tmp.pop()); // park smaller ones back on st
tmp.push(x);
}
while (!tmp.isEmpty()) st.push(tmp.pop()); // smallest goes in first, so largest ends on top
}
Dry run on Example 1 (stacks written top first):
| step | popped x | moved back to st | tmp after | st after |
|---|---|---|---|---|
| 1 | 41 | none | 41 | 3, 32, 2, 11 |
| 2 | 3 | none | 3, 41 | 32, 2, 11 |
| 3 | 32 | 3 | 32, 41 | 3, 2, 11 |
| 4 | 3 | none | 3, 32, 41 | 2, 11 |
| 5 | 2 | none | 2, 3, 32, 41 | 11 |
| 6 | 11 | 2, 3 | 11, 32, 41 | 3, 2 |
| 7 | 3 | none | 3, 11, 32, 41 | 2 |
| 8 | 2 | none | 2, 3, 11, 32, 41 | empty |
Pouring tmp back pushes 2, 3, 11, 32, 41 in that order, so the input becomes (top first) 41, 32, 11, 3, 2.
Complexity. Time O(n^2) in the worst case: an element can be moved back and forth many times (an input that arrives in the “wrong” order forces this). Space O(n) for tmp. No recursion, so no risk of overflowing the call stack.
Edge cases and pitfalls
- Empty stack and single element. Both are already sorted; every method leaves them unchanged. The tests include both.
- Direction of the order. This version puts the largest on top. If a judge wants the smallest on top, flip the comparisons (
≥ininsertSorted,>in the inner loop) rather than reversing at the end. - Duplicates.
insertSortedpushesxwhen the top is≤ x, and the temp-stack loop only moves elements strictly smaller thanx. Both avoid needless moves of equal values. The tests use[5, -1, 5, 0, -3]and 100 seeded random stacks with many repeats. - Why Approach 3 terminates. An element is moved back only when a larger value arrives, so the largest value never moves again once it reaches
tmp; after that the same holds for the second largest, and so on. Using≤in the inner loop would still terminate but would shuffle equal values pointlessly. - Recursion depth. Approach 2 nests up to about
2nframes. Prefer Approach 3 for large inputs.
Related problems
- Sort a stack using recursion is the A2Z version, focused on the recursive approach.
- Reverse a Stack uses the same “pop, recurse, insert at the right place” pattern, inserting at the bottom instead.
- Insertion Sorting is the array algorithm that Approach 2 reproduces with stacks.
- Implement Stack using Arrays covers the stack operations used here.