MinStack: Stack with O(1) Minimum

easyPython

Lesson

Auxiliary Data Structures for Optimization

Sometimes solving a problem efficiently requires thinking beyond the obvious data structure. A stack naturally supports push, pop, and top operations in O(1) time, but finding the minimum element would normally require scanning all elements - an O(n) operation.

The key insight is to use auxiliary data structures or additional storage to precompute and maintain information we'll need later. Instead of recalculating the minimum every time, we can store it alongside our data.

There are several approaches to this problem:

  1. Two Stack Approach: Keep one stack for values and another stack for minimums. When pushing, if the new value is ≤ current minimum, push it to both stacks. When popping, if the popped value equals the current minimum, pop from both stacks.

  2. Pair Storage Approach: Store each element as a pair: (value, minimum_at_this_level). Each entry remembers what the minimum was when that element was added.

  3. Difference Encoding: Store the difference between each element and the current minimum (more advanced).

The trade-off is space for time - we use extra memory to achieve O(1) time complexity. This pattern appears frequently in algorithm design: when direct computation is too slow, we often precompute or maintain additional information.

This technique is especially valuable in scenarios where the expensive operation (finding minimum) happens frequently, making the extra space worthwhile for the time savings.

Example
1# Example: Maintaining running maximum with auxiliary storage 2class RunningMax: 3 def __init__(self): 4 self.values = [] 5 self.maxes = [] # Auxiliary structure 6 7 def add(self, val): 8 self.values.append(val) 9 # Store the maximum seen so far 10 if not self.maxes: 11 current_max = val 12 else: 13 current_max = max(val, self.maxes[-1]) 14 self.maxes.append(current_max) 15 16 def get_max(self): 17 return self.maxes[-1] # O(1) lookup!
L4Auxiliary structure stores precomputed maximums
L10Compare with previous maximum, not all elements
L14Instant lookup thanks to precomputation

Key Takeaways

  • •Use auxiliary data structures to trade space for time when direct computation is too expensive
  • •Precompute information during modifications to make queries faster
  • •The pattern of storing 'state at each level' is common in stack-based problems
Loading...