Skip to content

Commit 9712c19

Browse files
committed
Add: Stack with Min in Python- Implements a LIFO stack supporting O(1) get_min operation- Time Complexity: Push O(1), Pop O(1), Top O(1), GetMin O(1)- Space Complexity: O(n)
1 parent bf9a928 commit 9712c19

File tree

1 file changed

+212
-0
lines changed

1 file changed

+212
-0
lines changed
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
# Python/data_structures/stack/stack_with_min.py
2+
3+
"""
4+
Data Structure Description:
5+
This implements a Stack (LIFO - Last-In, First-Out) with an additional
6+
operation `get_min()` that returns the minimum element currently in the stack
7+
in O(1) time complexity.
8+
9+
This is achieved by using an auxiliary stack (`_min_stack`) that stores
10+
the minimum value seen *so far* at each level of the main stack (`_main_stack`).
11+
When pushing an element, if it's less than or equal to the current minimum
12+
(the top of `_min_stack`), it's pushed onto `_min_stack` as well.
13+
When popping, if the element being popped is the current minimum (top of
14+
`_min_stack`), it's also popped from `_min_stack`.
15+
"""
16+
17+
# Time Complexity:
18+
# - Push (append): O(1)
19+
# - Pop: O(1)
20+
# - Top (peek): O(1)
21+
# - GetMin: O(1)
22+
# - is_empty: O(1)
23+
# - size: O(1)
24+
# Space Complexity: O(n)
25+
# - In the worst case (e.g., pushing decreasing numbers), the _min_stack
26+
# can store as many elements as the _main_stack.
27+
28+
class StackWithMin:
29+
"""Implements a stack supporting O(1) get_min operation."""
30+
31+
def __init__(self):
32+
"""Initializes the main stack and the auxiliary min stack."""
33+
self._main_stack = []
34+
self._min_stack = []
35+
36+
def push(self, item):
37+
"""Pushes an item onto the stack.
38+
>>> s = StackWithMin()
39+
>>> s.push(5)
40+
>>> s.push(2)
41+
>>> s.push(3)
42+
>>> s.push(1)
43+
>>> print(s._main_stack)
44+
[5, 2, 3, 1]
45+
>>> print(s._min_stack) # Stores min seen at each stage
46+
[5, 2, 1]
47+
"""
48+
self._main_stack.append(item)
49+
# Push to min_stack if it's empty or item is <= current min
50+
if not self._min_stack or item <= self._min_stack[-1]:
51+
self._min_stack.append(item)
52+
53+
def pop(self):
54+
"""Removes and returns the top item from the stack.
55+
Raises IndexError if the stack is empty.
56+
>>> s = StackWithMin()
57+
>>> s.push(5)
58+
>>> s.push(2)
59+
>>> s.push(1)
60+
>>> s.pop() # Removes 1 from both
61+
1
62+
>>> print(s._main_stack)
63+
[5, 2]
64+
>>> print(s._min_stack)
65+
[5, 2]
66+
>>> s.pop() # Removes 2 from both
67+
2
68+
>>> s.push(3)
69+
>>> s.pop() # Removes 3 from main, not min
70+
3
71+
>>> print(s._min_stack)
72+
[5]
73+
>>> s.pop() # Removes 5 from both
74+
5
75+
>>> s.pop()
76+
Traceback (most recent call last):
77+
...
78+
IndexError: pop from empty stack
79+
"""
80+
if not self._main_stack:
81+
raise IndexError("pop from empty stack")
82+
83+
item_to_pop = self._main_stack.pop()
84+
# If the popped item is the current minimum, pop from min_stack too
85+
if item_to_pop == self._min_stack[-1]:
86+
self._min_stack.pop()
87+
return item_to_pop
88+
89+
def top(self):
90+
"""Returns the top item of the stack without removing it.
91+
Raises IndexError if the stack is empty.
92+
>>> s = StackWithMin()
93+
>>> s.push(5)
94+
>>> s.push(2)
95+
>>> s.top()
96+
2
97+
>>> s.pop()
98+
2
99+
>>> s.top()
100+
5
101+
>>> s.pop()
102+
5
103+
>>> s.top()
104+
Traceback (most recent call last):
105+
...
106+
IndexError: top from empty stack
107+
"""
108+
if not self._main_stack:
109+
raise IndexError("top from empty stack")
110+
return self._main_stack[-1]
111+
112+
def get_min(self):
113+
"""Returns the minimum item currently in the stack in O(1).
114+
Raises IndexError if the stack is empty.
115+
>>> s = StackWithMin()
116+
>>> s.push(5)
117+
>>> s.get_min()
118+
5
119+
>>> s.push(2)
120+
>>> s.get_min()
121+
2
122+
>>> s.push(3)
123+
>>> s.get_min()
124+
2
125+
>>> s.push(1)
126+
>>> s.get_min()
127+
1
128+
>>> s.pop()
129+
1
130+
>>> s.get_min()
131+
2
132+
>>> s.pop()
133+
3
134+
>>> s.get_min()
135+
2
136+
>>> s.pop()
137+
2
138+
>>> s.get_min()
139+
5
140+
>>> s.pop()
141+
5
142+
>>> s.get_min()
143+
Traceback (most recent call last):
144+
...
145+
IndexError: get_min from empty stack
146+
"""
147+
if not self._min_stack:
148+
raise IndexError("get_min from empty stack")
149+
return self._min_stack[-1]
150+
151+
def is_empty(self):
152+
"""Returns True if the stack is empty, False otherwise.
153+
>>> s = StackWithMin()
154+
>>> s.is_empty()
155+
True
156+
>>> s.push(1)
157+
>>> s.is_empty()
158+
False
159+
"""
160+
return len(self._main_stack) == 0
161+
162+
def size(self):
163+
"""Returns the number of items in the stack.
164+
>>> s = StackWithMin()
165+
>>> s.size()
166+
0
167+
>>> s.push(1)
168+
>>> s.push(2)
169+
>>> s.size()
170+
2
171+
"""
172+
return len(self._main_stack)
173+
174+
def __len__(self):
175+
"""Allows using len(s)."""
176+
return self.size()
177+
178+
def __str__(self):
179+
"""String representation of the main stack."""
180+
return f"StackWithMin({self._main_stack})"
181+
182+
def __repr__(self):
183+
"""Representation showing internal state."""
184+
return f"StackWithMin(main={self._main_stack}, min={self._min_stack})"
185+
186+
187+
if __name__ == "__main__":
188+
import doctest
189+
doctest.testmod()
190+
191+
s = StackWithMin()
192+
print("Is empty?", s.is_empty())
193+
s.push(10)
194+
s.push(5)
195+
s.push(15)
196+
s.push(3) # New min
197+
print(s)
198+
print("Size:", len(s))
199+
print("Top:", s.top())
200+
print("Current Min:", s.get_min())
201+
print("Pop:", s.pop()) # Removes 3
202+
print(s)
203+
print("Current Min:", s.get_min()) # Should be 5
204+
print("Pop:", s.pop()) # Removes 15
205+
print("Current Min:", s.get_min()) # Still 5
206+
print("Pop:", s.pop()) # Removes 5
207+
print("Current Min:", s.get_min()) # Should be 10
208+
print("Is empty?", s.is_empty())
209+
print("Pop:", s.pop()) # Removes 10
210+
print("Is empty?", s.is_empty())
211+
# print("Pop:", s.pop()) # Raises IndexError
212+
# print("Get Min:", s.get_min()) # Raises IndexError

0 commit comments

Comments
 (0)