2023-10-06 00:27:22 -07:00
|
|
|
from __future__ import annotations
|
2023-10-06 16:55:07 -07:00
|
|
|
|
2023-10-06 00:27:22 -07:00
|
|
|
import math
|
|
|
|
|
|
|
|
"""
|
2023-10-06 16:55:07 -07:00
|
|
|
Python3 stack implementation that retrieves min/max
|
|
|
|
elements of the stack in constant time
|
2023-10-06 00:27:22 -07:00
|
|
|
"""
|
2023-10-06 16:55:07 -07:00
|
|
|
|
|
|
|
|
2023-10-06 00:27:22 -07:00
|
|
|
class StackData:
|
2023-10-06 16:55:07 -07:00
|
|
|
"""
|
|
|
|
Object stored on the stack
|
|
|
|
"""
|
|
|
|
|
2023-10-07 00:05:58 +00:00
|
|
|
def __init__(
|
|
|
|
self, current_value: float, min_value: float, max_value: float
|
|
|
|
) -> None:
|
2023-10-06 00:27:22 -07:00
|
|
|
self.current_value = current_value
|
|
|
|
self.min_value = min_value
|
|
|
|
self.max_value = max_value
|
|
|
|
|
2023-10-06 16:55:07 -07:00
|
|
|
|
2023-10-06 00:27:22 -07:00
|
|
|
class MinMaxStack:
|
2023-10-06 16:55:07 -07:00
|
|
|
"""
|
|
|
|
Main stack implementation
|
|
|
|
"""
|
|
|
|
|
2023-10-06 00:27:22 -07:00
|
|
|
def __init__(self, max_stack_size: int = 10) -> None:
|
|
|
|
self.max_size = max_stack_size
|
|
|
|
self.stack = []
|
|
|
|
|
|
|
|
def push_value(self, value: float) -> bool:
|
|
|
|
"""
|
2023-10-06 16:34:59 -07:00
|
|
|
Push new value on top of stack
|
|
|
|
|
2023-10-06 00:27:22 -07:00
|
|
|
>>> 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):
|
|
|
|
...
|
2023-10-06 16:59:15 -07:00
|
|
|
Exception: Max stack size reached.
|
2023-10-06 00:27:22 -07:00
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
if len(self.stack) == self.max_size:
|
2023-10-06 16:55:07 -07:00
|
|
|
raise Exception("Max stack size reached.")
|
2023-10-06 00:27:22 -07:00
|
|
|
|
|
|
|
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:
|
|
|
|
"""
|
2023-10-06 16:34:59 -07:00
|
|
|
Remove the top value from the stack.
|
|
|
|
|
2023-10-06 00:27:22 -07:00
|
|
|
>>> 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:
|
|
|
|
"""
|
2023-10-06 16:34:59 -07:00
|
|
|
Get the highest value on the stack in constant time
|
|
|
|
|
2023-10-06 16:55:07 -07:00
|
|
|
>>> test_stack = MinMaxStack(3)
|
2023-10-06 00:27:22 -07:00
|
|
|
>>> 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:
|
|
|
|
"""
|
2023-10-06 16:34:59 -07:00
|
|
|
Get the lowest value on the stack in constant time
|
|
|
|
|
2023-10-06 00:27:22 -07:00
|
|
|
>>> 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:
|
2023-10-06 16:34:59 -07:00
|
|
|
"""
|
|
|
|
Validate stack is not empty
|
|
|
|
"""
|
2023-10-06 00:27:22 -07:00
|
|
|
if len(self.stack) == 0:
|
|
|
|
print("Stack is empty")
|
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
from doctest import testmod
|
2023-10-06 16:55:07 -07:00
|
|
|
|
2023-10-07 00:05:58 +00:00
|
|
|
testmod()
|