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 <= 1000
0 <= val <= 100
1000
calls will be made to each method of increment
, push
and pop
each separately.This problem requires designing a stack data structure with the ability to perform increment operations on its elements. The solution uses an array-based approach to efficiently handle these operations. Let's break down the solution's logic and complexity analysis.
The core idea is to simulate a stack using an array (stk
) to store the elements and another array (add
) to track cumulative increments for each position in the stack. Instead of directly modifying the stack elements during increment operations, we store the increment values in the add
array. This allows us to efficiently calculate the actual value of an element when it's popped.
Data Structures:
stk
: An array to store the stack's elements.add
: An array to store the cumulative increment values for each position. add[i]
represents the total increment applied to elements at index i
or below.i
: An integer representing the current top of the stack (index of the next available position).Operations:
push(x)
: Adds x
to the top of the stack if there's space (i < maxSize
).pop()
: If the stack is not empty, it retrieves the element at the top (stk[i-1] + add[i-1]
), updates the add
array (propagating increments downward), and returns the value.increment(k, val)
: Increments the bottom k
elements by val
. It adds val
to add[min(i, k) - 1]
. This effectively adds val
to all elements at index min(i, k) - 1
and below.Time Complexity: All three operations (push
, pop
, increment
) have a time complexity of O(1) in the average case. This is because they involve a constant number of array accesses and arithmetic operations regardless of the stack size.
Space Complexity: The space complexity is O(maxSize), where maxSize
is the maximum size of the stack. This is due to the use of two arrays, stk
and add
, both of size maxSize
.
The Python3 and Java code implementations efficiently implement the described approach. The other languages (C++, Go, TypeScript) follow a very similar structure.
Python3:
class CustomStack:
def __init__(self, maxSize: int):
self.stk = [0] * maxSize
self.add = [0] * maxSize
self.i = 0
def push(self, x: int) -> None:
if self.i < len(self.stk):
self.stk[self.i] = x
self.i += 1
def pop(self) -> int:
if self.i <= 0:
return -1
self.i -= 1
ans = self.stk[self.i] + self.add[self.i]
if self.i > 0:
self.add[self.i - 1] += self.add[self.i]
self.add[self.i] = 0
return ans
def increment(self, k: int, val: int) -> None:
if self.i > 0:
self.add[min(k, self.i) - 1] += val
Java:
class CustomStack {
private int[] stk;
private int[] add;
private int i;
public CustomStack(int maxSize) {
stk = new int[maxSize];
add = new int[maxSize];
}
public void push(int x) {
if (i < stk.length) {
stk[i++] = x;
}
}
public int pop() {
if (i <= 0) {
return -1;
}
i--;
int ans = stk[i] + add[i];
if (i > 0) {
add[i - 1] += add[i];
}
add[i] = 0;
return ans;
}
public void increment(int k, int val) {
if (i > 0) {
add[Math.min(i, k) - 1] += val;
}
}
}
This detailed explanation, along with the code examples, provides a comprehensive understanding of the solution to the "Design a Stack with Increment Operation" problem. The efficient use of arrays and the clever handling of incremental updates contribute to the optimal time and space complexity of the solution.