Stacks & Queues · 5 of 38
Implement N stacks in an Array
Let many piles share one row of lockers, where any pile can take any empty locker and every locker remembers which one is under it.
- Simple way
- time O(1) per operation
room O(n + k), but each stack gets only n/k - Clever way
- time O(1) per operation
room O(n + k)
The problem, simply
You get one array with n boxes. Build k stacks inside it. You need push(s, x), pop(s) and peek(s) for any stack number s.
The rule: no stack may say "full" while there is still an empty box anywhere in the array. One busy stack should be able to use almost the whole array if the others are small.
A story to remember it
Picture it
Six boxes shared by three stacks. Stack 0 is 11 then 39, stack 1 is 17, stack 2 is 15 then 45. The next row says which box is under each one. free points at box 5, the only empty box.
Examples
A normal run
- In
n = 6, k = 3: push(2,15), push(2,45), push(0,11), push(1,17), push(0,39), pop(2)- Out
45
45 was the last push into stack 2, so it comes out first. The other stacks do not notice.
A reused box
- In
the run above, then push(1,49), peek(1)- Out
49, stored in box 1
Box 1 held 45 a moment ago. After the pop, it went back on the empty list, so stack 1 grabs it next.
One stack takes it all
- In
n = 3, k = 3: push(0,1), push(0,2), push(0,3)- Out
all three pushes work
Stack 0 uses every box. If each stack had its own fixed block of n/k = 1 box, the second push would already fail.
Full and empty
- In
after the example above: push(1,9), then pop(2)- Out
error: overflow, then error: stack 2 underflow
No box is free anywhere, so the push fails. Stack 2 never got anything, so the pop fails too.
Way 1: the simple way
Cut the array into k equal blocks of n/k boxes. Stack s lives in boxes s * n/k to (s + 1) * n/k - 1 and works like a normal array stack inside its block.
- Keep
tops[s], starting ats * size - 1(one before the block). - Push: if
tops[s]is at the end of the block, overflow. Elsearr[++tops[s]] = x. - Pop: if
tops[s]is before the block, underflow. Else returnarr[tops[s]--].
n = 6, k = 3, so each stack gets 2 boxes. Stack 0 is full and cannot take box 2, 3, 4 or 5, even though they are empty.
- rejected
- ignored
class KStacksBlocks {
private final int[] arr, tops;
private final int size; // boxes per stack
KStacksBlocks(int n, int k) {
arr = new int[n];
tops = new int[k];
size = n / k;
for (int s = 0; s < k; s++) tops[s] = s * size - 1;
}
void push(int s, int x) {
if (tops[s] == (s + 1) * size - 1) throw new RuntimeException("stack " + s + " overflow");
arr[++tops[s]] = x;
}
int pop(int s) {
if (tops[s] < s * size) throw new RuntimeException("stack " + s + " underflow");
return arr[tops[s]--];
}
}How long it takes: O(1) per action. Extra room: the array plus k tops, O(n + k). But a stack overflows after n/k items even when the rest of the array is empty.
Way 2: the clever way
Treat each box like a locker with a sticky note. Use three arrays:
arr[i]: the value in boxi.next[i]: for a used box, the box under it in the same stack. For an empty box, the next empty box.top[s]: the box on top of stacks, or-1if it is empty.
And one number, free, the first empty box. At the start every box is empty and they are chained 0, 1, 2, ....
push(s, x): iffree == -1, overflow. Else take boxi = free, movefree = next[i], writearr[i] = x, setnext[i] = top[s], thentop[s] = i.pop(s): iftop[s] == -1, underflow. Else takei = top[s], settop[s] = next[i], then put boxiback at the front of the empty chain:next[i] = free,free = i. Returnarr[i].
The empty boxes form a stack of their own. Push moves a box from the free stack to stack s, and pop moves it back.
Three stacks sharing six boxes
01 / 10
Start. Every box is empty. The free chain goes 0, 1, 2, 3, 4, 5. All three tops are -1.
- looking now
- done
- rejected
- just added
class KStacks {
private final int[] arr, next, top;
private int free; // first empty box, -1 if none
KStacks(int n, int k) {
arr = new int[n];
next = new int[n];
top = new int[k];
Arrays.fill(top, -1);
for (int i = 0; i < n - 1; i++) next[i] = i + 1; // chain all empty boxes
next[n - 1] = -1;
free = 0;
}
void push(int s, int x) {
if (free == -1) throw new RuntimeException("overflow");
int i = free;
free = next[i]; // take box i off the empty chain
arr[i] = x;
next[i] = top[s]; // the old top is now under box i
top[s] = i;
}
int pop(int s) {
if (top[s] == -1) throw new RuntimeException("stack " + s + " underflow");
int i = top[s];
top[s] = next[i]; // the box under becomes the top
next[i] = free; // box i goes back on the empty chain
free = i;
return arr[i];
}
int peek(int s) {
if (top[s] == -1) throw new RuntimeException("stack " + s + " is empty");
return arr[top[s]];
}
boolean isEmpty(int s) { return top[s] == -1; }
boolean isFull() { return free == -1; }
}How long it takes: every action changes a few numbers, O(1). Extra room: arr and next of size n, plus top of size k, so O(n + k). Any stack can grow until the whole array is used.
Watch out for
- In
push, readfree = next[i]before writingnext[i] = top[s], for the same reason. - The
nextarray means two things: "box under me" for used boxes and "next empty box" for empty ones. A box is only ever on one chain, so that is safe. - Check
free == -1before pushing, andtop[s] == -1before popping. - The old value stays in
arrafter a pop. That is fine, because nothing points to it.
What interviewers ask next
- Do the same for
kqueues. Keepfront[q]andrear[q], and link each new box afterrear[q]. The free chain works the same way. - Why not use
kseparateArrayLists? In real code you would. This problem is about memory tricks for systems with one fixed block of memory, like an embedded device or an allocator. - What is the free chain called? A free list. Memory allocators and object pools use it to hand out and take back fixed size blocks in
O(1). - Can you give the size of each stack in
O(1)? Keep acount[s]array and update it in push and pop.
Remember this
Grown-up words: the lockers are array slots, the sticky notes are the next array (an index based linked list), the list of empty lockers is a free list, and top[s] is the head of stack s.