Design a stack that supports increment operations on its elements.
Implement the CustomStack class:
CustomStack(int maxSize) Initializes the object with maxSize which is the maximum number of elements in the stack.void push(int x) Adds x to the top of the stack if the stack has not reached the maxSize.int pop() Pops and returns the top of the stack or -1 if the stack is empty.void inc(int k, int val) Increments the bottom k elements of the stack by val. If there are less than k elements in the stack, increment all the elements in the stack.Example 1:
Input ["CustomStack","push","push","pop","push","push","push","increment","increment","pop","pop","pop","pop"] [[3],[1],[2],[],[2],[3],[4],[5,100],[2,100],[],[],[],[]] Output [null,null,null,2,null,null,null,null,null,103,202,201,-1] Explanation CustomStack stk = new CustomStack(3); // Stack is Empty [] stk.push(1); // stack becomes [1] stk.push(2); // stack becomes [1, 2] stk.pop(); // return 2 --> Return top of the stack 2, stack becomes [1] stk.push(2); // stack becomes [1, 2] stk.push(3); // stack becomes [1, 2, 3] stk.push(4); // stack still [1, 2, 3], Do not add another elements as size is 4 stk.increment(5, 100); // stack becomes [101, 102, 103] stk.increment(2, 100); // stack becomes [201, 202, 103] stk.pop(); // return 103 --> Return top of the stack 103, stack becomes [201, 202] stk.pop(); // return 202 --> Return top of the stack 202, stack becomes [201] stk.pop(); // return 201 --> Return top of the stack 201, stack becomes [] stk.pop(); // return -1 --> Stack is empty return -1.
Constraints:
1 <= maxSize, x, k <= 10000 <= val <= 1001000 calls will be made to each method of increment, push and pop each separately.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
We can simulate the stack's behavior directly by keeping track of each element's current value. When an increment operation happens, we can re-calculate the final value for all affected elements.
Here's how the algorithm would work step-by-step:
class CustomStack:
def __init__(self, maxSize):
self.stack_elements = []
self.maximum_size = maxSize
def push(self, x):
# Ensure we do not exceed the maximum allowed size of the stack
if len(self.stack_elements) < self.maximum_size:
self.stack_elements.append(x)
def pop(self):
# Only pop if there are elements available in the stack
if self.stack_elements:
return self.stack_elements.pop()
return -1
def increment(self, k, val):
# Determine the actual number of elements to increment to avoid index out of bounds
elements_to_increment = min(k, len(self.stack_elements))
for i in range(elements_to_increment):
self.stack_elements[i] += val
def peek(self):
# Return the top element without removing it, or -1 if the stack is empty
if self.stack_elements:
return self.stack_elements[-1]
return -1We can create a stack that supports an increment operation by tracking how much to add to each element. Instead of directly updating every element in the stack when an increment is requested, we defer the addition until it's absolutely necessary, like when we remove an element.
Here's how the algorithm would work step-by-step:
class CustomStack:
def __init__(self, maxSize):
self.maximum_stack_size = maxSize
self.stack_elements = []
# Deferred increments are stored per element index.
self.deferred_increments = [0] * maxSize
def push(self, element_value):
if len(self.stack_elements) < self.maximum_stack_size:
self.stack_elements.append(element_value)
def pop(self):
if not self.stack_elements:
return -1
current_index = len(self.stack_elements) - 1
increment_to_apply = self.deferred_increments[current_index]
# Pass down the increment to the element below if it exists.
if current_index > 0:
self.deferred_increments[current_index - 1] += increment_to_apply
# Reset the increment for the current element as it's being processed.
self.deferred_increments[current_index] = 0
return self.stack_elements.pop() + increment_to_apply
def increment(self, lower_bound_index, increment_value):
# Only apply increment if the lower bound is within stack limits.
if lower_bound_index < len(self.stack_elements):
self.deferred_increments[min(lower_bound_index, len(self.stack_elements) - 1)] += increment_value
| Case | How to Handle |
|---|---|
| Pushing to a full stack | The push operation should simply do nothing and not add the element to maintain the maxSize constraint. |
| Popping from an empty stack | The pop operation should return a sentinel value (e.g., -1) or throw an exception to indicate an invalid operation. |
| Incrementing more elements than present in the stack (k > current stack size) | The increment operation should apply to all existing elements in the stack if k exceeds the current number of elements. |
| Incrementing zero or a negative number of elements (k <= 0) | The increment operation should have no effect if k is zero or negative. |
| Incrementing with a zero or negative value (val == 0 or val < 0) | The increment operation should correctly add zero or the negative value to the specified bottom elements. |
| Stack with maxSize of 0 | The stack should behave as if it can never hold any elements, with push operations always failing and pop operations always returning a sentinel. |
| Multiple consecutive push and pop operations | The underlying data structure must efficiently handle the dynamic resizing and indexing required for these mixed operations. |
| Large number of elements and frequent increment operations | The increment operation's time complexity should be considered to ensure overall efficiency, potentially requiring an optimized approach like lazy propagation. |