""" Python3 stack implementation that retrieves min/max elements of the stack in constant time """ from __future__ import annotations import math """ Data object for stack """ class StackData: def __init__(self, current_value: float, min_value: float, max_value: float) -> None: self.current_value = current_value self.min_value = min_value self.max_value = max_value """ Main stack implementation """ class MinMaxStack: def __init__(self, max_stack_size: int = 10) -> None: self.max_size = max_stack_size self.stack = [] def push_value(self, value: float) -> bool: """ >>> test_stack = MinMaxStack(3) >>> test_stack.push_value(1) True >>> test_stack.push_value(2) True >>> test_stack.push_value(3) True >>> test_stack.push_value(4) Traceback (most recent call last): ... Exception: Max stack size reached. Remove elements before adding new ones or increase stack size. """ if len(self.stack) == self.max_size: raise Exception("Max stack size reached. Remove elements before adding new ones or increase stack size.") if len(self.stack) == 0: self.stack.append(StackData(value, value, value)) else: """Store min/max values with current value""" new_min = min(self.stack[-1].min_value, value) new_max = max(self.stack[-1].max_value, value) self.stack.append(StackData(value, new_min, new_max)) return True def pop_value(self) -> float: """ >>> test_stack = MinMaxStack() >>> test_stack.push_value(1) True >>> test_stack.push_value(2) True >>> test_stack.pop_value() 2 >>> test_stack.pop_value() 1 >>> test_stack.pop_value() Stack is empty -inf """ if self.stack_is_valid(): return self.stack.pop().current_value return -math.inf def get_current_max(self) -> float: """ test_stack = MinMaxStack(3) >>> test_stack.push_value(-450.45) True >>> test_stack.push_value(450.45) True >>> test_stack.push_value(0) True >>> test_stack.get_current_max() 450.45 >>> test_stack.pop_value() 0 >>> test_stack.get_current_max() 450.45 >>> test_stack.pop_value() 450.45 >>> test_stack.get_current_max() -450.45 >>> test_stack.pop_value() -450.45 >>> test_stack.get_current_max() Stack is empty -inf """ if self.stack_is_valid(): return self.stack[-1].max_value return -math.inf def get_current_min(self) -> float: """ >>> test_stack = MinMaxStack(3) >>> test_stack.push_value(123) True >>> test_stack.push_value(-123) True >>> test_stack.push_value(0) True >>> test_stack.get_current_min() -123 >>> test_stack.pop_value() 0 >>> test_stack.get_current_min() -123 >>> test_stack.pop_value() -123 >>> test_stack.get_current_min() 123 >>> test_stack.pop_value() 123 >>> test_stack.get_current_min() Stack is empty -inf """ if self.stack_is_valid(): return self.stack[-1].min_value return -math.inf def stack_is_valid(self) -> bool: if len(self.stack) == 0: print("Stack is empty") return False return True if __name__ == "__main__": from doctest import testmod testmod()