Imagine a (literal) stack of plates. If the stack gets too high, it might topple. Therefore, in real life, we would likely start a new stack when the previous stack exceeds some threshold.
Implement a data structure SetOfStacks that mimics this. SetOfStacks should be composed of several stacks, and should create a new stack once the previous one exceeds capacity. SetOfStacks.push() and SetOfStacks.pop() should behave identically to a single stack (that is, pop() should return the same values as it would if there were just a single stack).
Follow up: Implement a function popAt(int index) which performs a pop operation on a specific sub-stack.
Input: capacity =3Operations: push(1), push(2), push(3), push(4), push(5)Output: Two stacks - Stack 1:[1,2,3], Stack 2:[4,5]Explanation: After pushing 3 elements, capacity is reached, so a new stack is created.
We need to maintain multiple stacks where each has a fixed capacity. When the current stack reaches capacity, we create a new one. The key operations are:
Push: Add to the last stack if it has space, otherwise create a new stack
Pop: Remove from the last stack, and remove empty stacks
For the follow-up challenge, we might want to implement a more sophisticated version where popAt operations cause elements to “roll over” from subsequent stacks to maintain consistent stack sizes. This approach ensures that we don’t have gaps in the middle stacks.
classNode:
def__init__(self, value: int):
self.value = value
self.above =None self.below =NoneclassStackNode:
def__init__(self, capacity: int):
self.capacity = capacity
self.size =0 self.top =None self.bottom =Nonedefis_full(self) -> bool:
return self.size == self.capacity
defis_empty(self) -> bool:
return self.size ==0defpush(self, value: int) -> bool:
if self.is_full():
returnFalse node = Node(value)
if self.size ==0:
self.bottom = node
else:
self.top.above = node
node.below = self.top
self.top = node
self.size +=1returnTruedefpop(self) -> int:
if self.is_empty():
raiseException("Stack is empty")
value = self.top.value
self.top = self.top.below
if self.top:
self.top.above =Noneelse:
self.bottom =None self.size -=1return value
defremove_bottom(self) -> int:
if self.is_empty():
raiseException("Stack is empty")
value = self.bottom.value
self.bottom = self.bottom.above
if self.bottom:
self.bottom.below =Noneelse:
self.top =None self.size -=1return value
classSetOfStacksWithRollover:
def__init__(self, capacity: int):
self.capacity = capacity
self.stacks = []
defpush(self, value: int) ->None:
ifnot self.stacks or self.stacks[-1].is_full():
self.stacks.append(StackNode(self.capacity))
self.stacks[-1].push(value)
defpop(self) -> int:
ifnot self.stacks:
raiseException("No elements to pop")
value = self.stacks[-1].pop()
if self.stacks[-1].is_empty():
self.stacks.pop()
return value
defpop_at(self, index: int) -> int:
if index <0or index >= len(self.stacks):
raiseException("Invalid stack index")
value = self.stacks[index].pop()
self._shift_left(index)
return value
def_shift_left(self, index: int) ->None:
"""Shift elements left to fill gaps after popAt"""for i in range(index, len(self.stacks) -1):
ifnot self.stacks[i +1].is_empty():
# Move bottom element from next stack to current stack value = self.stacks[i +1].remove_bottom()
self.stacks[i].push(value)
# Remove last stack if it becomes emptyif self.stacks and self.stacks[-1].is_empty():
self.stacks.pop()
defis_empty(self) -> bool:
return len(self.stacks) ==0defsize(self) -> int:
return sum(stack.size for stack in self.stacks)
# Example usagedeftest_rollover_stacks():
stacks = SetOfStacksWithRollover(3)
# Push elements 1-9for i in range(1, 10):
stacks.push(i)
print(f"Initial size: {stacks.size()}")
# Pop from middle stack - should cause rollover print(f"PopAt(1): {stacks.pop_at(1)}")
print(f"Size after popAt: {stacks.size()}")
if __name__ =="__main__":
test_rollover_stacks()