As part of an online programming challenge, I wrote a program that implements a stack of integers, supporting adding and removing elements, finding the last inserted element, and the minimum element. The premise is that all methods must be implemented in constant O(1) time.
In my code, I opted to use LinkedList
s for the internal stacks, as with an array-based data structure such as ArrayDeque
, ArrayList
, and the legacy Stack
, there's the risk of running out of internal capacity in the specific case of adding a number, which would thus make it have a potential time complexity of O(n) (worst case). That class also conveniently implements the Deque
interface, which doubles as the standard generic interface for stacks.
In my solution, I make use of three stacks: the primary internal stack used for all methods, a stack with the last known minimum number, and one that keeps count of the amount of numbers added since the last known minimum number was added (that is, the number of times to pop before the top/last entry in the known minimum stack is no longer in the primary stack). I keep track of the topmost minimum as a variable so it can be modified on the fly easily, and only when a new minimum is added do I push that value onto the third stack (and reset the variable to 0).
Here's my code:
import java.util.Deque;
import java.util.LinkedList;
class MinStack {
private Deque<Integer> stack;
private Deque<Integer> min;
private Deque<Integer> minStepsCount;
private int minSteps;
public MinStack() {
stack = new LinkedList<>();
min = new LinkedList<>();
minStepsCount = new LinkedList<>();
minSteps = 0;
}
public void push(int val) {
stack.push(val);
if (min.isEmpty() || min.peek() > val) {
min.push(val);
minStepsCount.push(minSteps);
minSteps = 0;
}
else
minSteps++;
}
public void pop() {
stack.pop();
if (minSteps == 0) {
min.pop();
minSteps = minStepsCount.pop();
}
else
minSteps--;
}
public int top() {
return stack.peek();
}
public int getMin() {
return min.peek();
}
}
The code functions correctly and passes all test cases, including the stringent time limits to ensure O(1) compliance. However, when it comes to memory usage, when considering all others' solutions to the problem, they fall into a bimodal distribution, meaning that solutions to the problem fall into two clear, distinct categories, with one taking up less memory and one taking up more memory. In this case, the less-memory solution uses an average of 40 MB, while the more-memory solution uses an average of 45 MB. My solution falls into the more-memory category.
What can I do to reduce the memory usage of this program so it falls into the less-memory category, and is there anything else that could use improvement in my code?