Dinner Plate Stacks

hard design heap stack

Problem

You have an infinite number of stacks arranged in a row, each with the same maximum capacity. Implement push(val) which pushes onto the leftmost stack that is not full, pop() which pops from the rightmost non-empty stack, and popAtStack(index) which pops from the stack at a given index. Any operation on an empty/non-existent stack returns -1.

Inputcapacity = 2; push(1) push(2) push(3) push(4) push(5); popAtStack(0); push(20) push(21); popAtStack(0); popAtStack(2); pop() pop() pop() pop() pop()
Output2, 20, 21, 5, 4, 3, 1, -1
With capacity 2 the first five pushes fill stacks [1,2] [3,4] [5]. popAtStack(0) removes 2, freeing index 0, so push(20) lands there; push(21) then fills it. The pops then drain the rightmost stacks until everything is empty.

import heapq

class DinnerPlates:
    def __init__(self, capacity):
        self.cap = capacity
        self.stacks = []
        self.avail = []  # min-heap of non-full stack indices

    def push(self, val):
        while self.avail and (self.avail[0] >= len(self.stacks)
                              or len(self.stacks[self.avail[0]]) == self.cap):
            heapq.heappop(self.avail)
        if not self.avail:
            self.stacks.append([])
            heapq.heappush(self.avail, len(self.stacks) - 1)
        i = self.avail[0]
        self.stacks[i].append(val)
        if len(self.stacks[i]) == self.cap:
            heapq.heappop(self.avail)

    def pop(self):
        while self.stacks and not self.stacks[-1]:
            self.stacks.pop()
        if not self.stacks:
            return -1
        return self.popAtStack(len(self.stacks) - 1)

    def popAtStack(self, index):
        if index < 0 or index >= len(self.stacks) or not self.stacks[index]:
            return -1
        heapq.heappush(self.avail, index)
        return self.stacks[index].pop()
class MinHeap {
  constructor() { this.a = []; }
  size() { return this.a.length; }
  peek() { return this.a[0]; }
  push(x) {
    const a = this.a; a.push(x); let i = a.length - 1;
    while (i > 0) { const p = (i - 1) >> 1; if (a[p] <= a[i]) break; [a[p], a[i]] = [a[i], a[p]]; i = p; }
  }
  pop() {
    const a = this.a, top = a[0], last = a.pop();
    if (a.length) { a[0] = last; let i = 0;
      for (;;) { let l = 2*i+1, r = 2*i+2, m = i;
        if (l < a.length && a[l] < a[m]) m = l;
        if (r < a.length && a[r] < a[m]) m = r;
        if (m === i) break; [a[m], a[i]] = [a[i], a[m]]; i = m; } }
    return top;
  }
}
class DinnerPlates {
  constructor(capacity) { this.cap = capacity; this.stacks = []; this.avail = new MinHeap(); }
  push(val) {
    const av = this.avail, st = this.stacks;
    while (av.size() && (av.peek() >= st.length || st[av.peek()].length === this.cap)) av.pop();
    if (!av.size()) { st.push([]); av.push(st.length - 1); }
    const i = av.peek();
    st[i].push(val);
    if (st[i].length === this.cap) av.pop();
  }
  pop() {
    const st = this.stacks;
    while (st.length && st[st.length - 1].length === 0) st.pop();
    if (!st.length) return -1;
    return this.popAtStack(st.length - 1);
  }
  popAtStack(index) {
    const st = this.stacks;
    if (index < 0 || index >= st.length || st[index].length === 0) return -1;
    this.avail.push(index);
    return st[index].pop();
  }
}
class DinnerPlates {
    int cap;
    List<Deque<Integer>> stacks = new ArrayList<>();
    PriorityQueue<Integer> avail = new PriorityQueue<>();

    public DinnerPlates(int capacity) { cap = capacity; }

    public void push(int val) {
        while (!avail.isEmpty() && (avail.peek() >= stacks.size()
                || stacks.get(avail.peek()).size() == cap))
            avail.poll();
        if (avail.isEmpty()) {
            stacks.add(new ArrayDeque<>());
            avail.offer(stacks.size() - 1);
        }
        int i = avail.peek();
        stacks.get(i).push(val);
        if (stacks.get(i).size() == cap) avail.poll();
    }

    public int pop() {
        while (!stacks.isEmpty() && stacks.get(stacks.size() - 1).isEmpty())
            stacks.remove(stacks.size() - 1);
        if (stacks.isEmpty()) return -1;
        return popAtStack(stacks.size() - 1);
    }

    public int popAtStack(int index) {
        if (index < 0 || index >= stacks.size() || stacks.get(index).isEmpty())
            return -1;
        avail.offer(index);
        return stacks.get(index).pop();
    }
}
class DinnerPlates {
    int cap;
    vector<vector<int>> stacks;
    priority_queue<int, vector<int>, greater<int>> avail;
public:
    DinnerPlates(int capacity) : cap(capacity) {}

    void push(int val) {
        while (!avail.empty() && (avail.top() >= (int)stacks.size()
                || (int)stacks[avail.top()].size() == cap))
            avail.pop();
        if (avail.empty()) {
            stacks.push_back({});
            avail.push((int)stacks.size() - 1);
        }
        int i = avail.top();
        stacks[i].push_back(val);
        if ((int)stacks[i].size() == cap) avail.pop();
    }

    int pop() {
        while (!stacks.empty() && stacks.back().empty()) stacks.pop_back();
        if (stacks.empty()) return -1;
        return popAtStack((int)stacks.size() - 1);
    }

    int popAtStack(int index) {
        if (index < 0 || index >= (int)stacks.size() || stacks[index].empty())
            return -1;
        avail.push(index);
        int v = stacks[index].back();
        stacks[index].pop_back();
        return v;
    }
};
Time: O(log n) amortized per op Space: O(n)