Stacks & Queues · 3 of 38
Implement 2 stack in an array
Two kids share one long shelf for their toy piles, one starting from each end, so no space is wasted until the piles meet.
- Simple way
- time O(1) per operation
room O(n), but each stack gets only n/2 - Clever way
- time O(1) per operation
room O(n), fully shared
The problem, simply
You get one array of size n. Build two stacks inside it. You need push1, pop1, push2 and pop2.
The catch: the array should only be "full" when all n boxes are used. If stack 1 is empty, stack 2 should be allowed to use every box.
A story to remember it
Picture it
An array of 6 boxes. Stack 1 grows from the left (top1), stack 2 grows from the right (top2). The two middle boxes are still free for either one.
- looking now
- done
Examples
A normal run
- In
n = 5: push1(1), push1(2), push2(9), pop1(), pop2()- Out
pop1 = 2, pop2 = 9
Each stack keeps its own order. Stack 1 has 2 on top. Stack 2 has 9 on top.
One stack uses everything
- In
n = 4: push2(1), push2(2), push2(3), push2(4)- Out
all four pushes work
Stack 1 is empty, so stack 2 may fill the whole array. Cutting the array in half would fail on the third push.
The piles meet
- In
n = 2: push1(1), push2(2), push1(3)- Out
error: overflow on push1(3)
Both boxes are used. top1 + 1 equals top2, so there is no room for anyone.
Pop an empty stack
- In
pop2() on a new array- Out
error: stack 2 underflow
top2 still sits just past the right end, which means stack 2 is empty.
Way 1: the simple way
Cut the array in half. Stack 1 lives in boxes 0 to n/2 - 1. Stack 2 lives in boxes n/2 to n - 1. Each half works like a normal array stack.
top1starts at-1.top2starts atn/2 - 1.push1fails whentop1reachesn/2 - 1.push2fails whentop2reachesn - 1.
Halves with n = 6. Stack 2 is full after three pushes, even though stack 1 has three empty boxes it will never share.
- rejected
- ignored
class TwoStacksHalves {
private final int[] arr;
private final int mid;
private int top1, top2;
TwoStacksHalves(int n) {
arr = new int[n];
mid = n / 2;
top1 = -1; // stack 1 uses 0 .. mid-1
top2 = mid - 1; // stack 2 uses mid .. n-1
}
void push1(int x) {
if (top1 == mid - 1) throw new RuntimeException("stack 1 overflow");
arr[++top1] = x;
}
void push2(int x) {
if (top2 == arr.length - 1) throw new RuntimeException("stack 2 overflow");
arr[++top2] = x;
}
int pop1() {
if (top1 < 0) throw new RuntimeException("stack 1 underflow");
return arr[top1--];
}
int pop2() {
if (top2 < mid) throw new RuntimeException("stack 2 underflow");
return arr[top2--];
}
}How long it takes: every action is O(1). Extra room: the array of n, but each stack can only ever hold n/2 items, so half the room may be wasted.
Way 2: the clever way
Grow the two stacks toward each other.
top1starts at-1, just left of the array.top2starts atn, just right of it.push1(x): iftop1 + 1 == top2, the piles touch, so overflow. Elsearr[++top1] = x.push2(x): same check. Elsearr[--top2] = x.pop1(): iftop1 == -1it is empty. Else returnarr[top1--].pop2(): iftop2 == nit is empty. Else returnarr[top2++].
The free space is always the gap between top1 and top2, and either stack may take it.
Two piles growing toward each other in 6 boxes
01 / 09
Empty array. top1 is just left of box 0, top2 is just right of box 5. The whole array is free.
- free
- 6
- looking now
- done
- rejected
- just added
- ignored
class TwoStacks {
private final int[] arr;
private int top1, top2;
TwoStacks(int n) {
arr = new int[n];
top1 = -1; // just left of the array
top2 = n; // just right of the array
}
void push1(int x) {
if (top1 + 1 == top2) throw new RuntimeException("overflow");
arr[++top1] = x; // grow to the right
}
void push2(int x) {
if (top1 + 1 == top2) throw new RuntimeException("overflow");
arr[--top2] = x; // grow to the left
}
int pop1() {
if (top1 == -1) throw new RuntimeException("stack 1 underflow");
return arr[top1--];
}
int pop2() {
if (top2 == arr.length) throw new RuntimeException("stack 2 underflow");
return arr[top2++];
}
int size1() { return top1 + 1; }
int size2() { return arr.length - top2; }
}How long it takes: every action is O(1). Extra room: the one array of n, and every box can be used by either stack.
Watch out for
- Stack 2 moves the other way. Push is
arr[--top2](step left first), pop isarr[top2++](read, then step right). Swapping the order of--and the read is the most common slip. - Stack 2 is empty when
top2 == n, not whentop2 == -1. - Size of stack 2 is
n - top2.
What interviewers ask next
- Can you put three stacks in one array the same way? Not with this trick. There are only two ends. For
kstacks, link free boxes together in a chain, which is the "n stacks in an array" problem. - What if a stack grows past the array? Make a bigger array, copy stack 1 to its left side and stack 2 to its right side, and fix
top2. - Can a queue and a stack share an array like this? A stack from one end works, but a queue moves at both of its ends, so it drifts. It is messy and rarely worth it.
- Where is this used? Old compilers and some memory managers put two growing areas, like the call stack and the heap, at opposite ends of memory for the same reason.
Remember this
Grown-up words: the shelf is the array, each toy pile is a stack, the two walls are the two ends of the array, top1 and top2 are the top indices, and running out of room is overflow.