Skip to content

Commit 529c17a

Browse files
authored
Miguel 3.1 - Three in one [Python] (#82)
Implementation of triple stack is in StackTrio class, corresponding test cases at the bottom
1 parent c177033 commit 529c17a

File tree

2 files changed

+452
-0
lines changed

2 files changed

+452
-0
lines changed
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
"""Python version 3.7.0
2+
3.1 - Three in one
3+
Describe how you could use a single array to implement three stacks
4+
"""
5+
6+
import copy
7+
import unittest
8+
9+
from dataclasses import dataclass
10+
from typing import Generic, TypeVar
11+
from typing import List, Optional, Iterator
12+
13+
T = TypeVar('T')
14+
15+
@dataclass
16+
class StackNode(Generic[T]):
17+
data: T
18+
next: 'Optional[StackNode[T]]'
19+
20+
@dataclass
21+
class StackInfo:
22+
id: int
23+
start: int
24+
end: int
25+
size: int
26+
top_index: int
27+
top_index_next: int
28+
29+
def __init__(self, **kwargs):
30+
self.__dict__.update(kwargs)
31+
32+
33+
class StackTrio:
34+
def __init__(self, stack_capacity = 7):
35+
self.num_stacks = 3
36+
self.stack_capacity = stack_capacity
37+
first_stack_info = StackInfo(
38+
id=1,
39+
start=0,
40+
end=stack_capacity - 1,
41+
size=0,
42+
top_index=0,
43+
top_index_next=-1
44+
)
45+
second_stack_info = StackInfo(
46+
id=2,
47+
start=stack_capacity,
48+
end=stack_capacity * 2 - 1,
49+
size=0,
50+
top_index=stack_capacity,
51+
top_index_next=-1
52+
)
53+
third_stack_info = StackInfo(
54+
id=3,
55+
start=stack_capacity * 2,
56+
end=stack_capacity * 3 - 1,
57+
size=0,
58+
top_index=stack_capacity * 2,
59+
top_index_next=-1
60+
)
61+
self.stack_info = {
62+
1: first_stack_info,
63+
2: second_stack_info,
64+
3: third_stack_info
65+
}
66+
self.values = [0] * (stack_capacity * self.num_stacks)
67+
68+
def _validate_stack_id(self, stack_id: int):
69+
"""Helper method to make sure stack_id
70+
doesn't go out of range.
71+
72+
Args:
73+
stack_id (int): id used to refer to correct stack valid ids: 1-3
74+
"""
75+
if stack_id < 1 or stack_id > 3:
76+
raise IndexError(f'Stack Id out of range. Valid ranges: 1-3, input: {stack_id}')
77+
78+
def is_empty(self, stack_id: int):
79+
self._validate_stack_id(stack_id)
80+
return self.stack_info[stack_id].size <= 0
81+
82+
def peek(self, stack_id):
83+
# this method returns the value at the top index
84+
self._validate_stack_id(stack_id)
85+
if self.is_empty(stack_id):
86+
raise IndexError('Stack is empty. Stack ID: {}'.format(stack_id))
87+
return self.values[self.stack_info[stack_id].top_index]
88+
89+
def push(self, stack_id, value):
90+
self._validate_stack_id(stack_id)
91+
# then, check if stack of interest is full
92+
if self.stack_info[stack_id].size >= self.stack_capacity:
93+
raise IndexError('Stack is full. Stack ID: {}'.format(stack_id))
94+
stack_top_index = self.stack_info[stack_id].top_index
95+
new_stack_top_index = stack_top_index + 1
96+
# if empty, then top index and next index stay the same.
97+
if self.is_empty(stack_id):
98+
self.values[stack_top_index] = value
99+
# update stack size.
100+
self.stack_info[stack_id].size += 1
101+
return
102+
103+
# otherwise, stack is not empty, and so we
104+
# first need to update the next and top indices, then update
105+
# the values accordingly.
106+
self.stack_info[stack_id].top_index_next = stack_top_index
107+
self.stack_info[stack_id].top_index = new_stack_top_index
108+
self.values[new_stack_top_index] = value
109+
self.stack_info[stack_id].size += 1
110+
111+
112+
113+
def pop(self, stack_id):
114+
self._validate_stack_id(stack_id)
115+
# then, make sure we are not at an empty stack
116+
if self.is_empty(stack_id):
117+
raise IndexError('Stack is empty. Stack ID: {}'.format(stack_id))
118+
# basically, the next index will be one less than top index.
119+
# when we pop, each top index will get decremented by 1
120+
original_stack_top_index = self.stack_info[stack_id].top_index
121+
val_before_pop = self.peek(stack_id)
122+
self.stack_info[stack_id].top_index -= 1
123+
self.stack_info[stack_id].top_index_next -= 1
124+
# clear value
125+
self.values[original_stack_top_index] = 0
126+
# decrement size
127+
self.stack_info[stack_id].size -= 1
128+
return val_before_pop
129+
130+
def get_size(self, stack_id):
131+
self._validate_stack_id(stack_id)
132+
return self.stack_info[stack_id].size
133+
134+
def __str__(self):
135+
"""
136+
We will print out the values
137+
as well as the stack info for each stack
138+
"""
139+
d = copy.deepcopy(self.stack_info)
140+
d['values'] = str(self.values)
141+
return str(d)
142+
143+
144+
class TestThreeInOne(unittest.TestCase):
145+
146+
def test_stack_push(self):
147+
148+
s_trio = StackTrio()
149+
150+
self.assertEqual(s_trio.get_size(1), 0)
151+
self.assertEqual(s_trio.get_size(2), 0)
152+
self.assertEqual(s_trio.get_size(3), 0)
153+
154+
s_trio.push(1, 99)
155+
156+
self.assertEqual(s_trio.get_size(1), 1)
157+
self.assertEqual(s_trio.get_size(2), 0)
158+
self.assertEqual(s_trio.get_size(3), 0)
159+
160+
s_trio.push(1, 100)
161+
self.assertEqual(s_trio.get_size(1), 2)
162+
self.assertEqual(s_trio.get_size(2), 0)
163+
self.assertEqual(s_trio.get_size(3), 0)
164+
165+
s_trio.push(2, 101)
166+
167+
self.assertEqual(s_trio.get_size(1), 2)
168+
self.assertEqual(s_trio.get_size(2), 1)
169+
self.assertEqual(s_trio.get_size(3), 0)
170+
171+
s_trio.push(2, 102)
172+
self.assertEqual(s_trio.get_size(1), 2)
173+
self.assertEqual(s_trio.get_size(2), 2)
174+
self.assertEqual(s_trio.get_size(3), 0)
175+
176+
s_trio.push(3, 103)
177+
178+
self.assertEqual(s_trio.get_size(1), 2)
179+
self.assertEqual(s_trio.get_size(2), 2)
180+
self.assertEqual(s_trio.get_size(3), 1)
181+
182+
s_trio.push(3, 104)
183+
self.assertEqual(s_trio.get_size(1), 2)
184+
self.assertEqual(s_trio.get_size(2), 2)
185+
self.assertEqual(s_trio.get_size(3), 2)
186+
187+
188+
def test_stack_peek(self):
189+
s_trio = StackTrio()
190+
191+
self.assertRaises(IndexError, lambda: s_trio.peek(1))
192+
self.assertRaises(IndexError, lambda: s_trio.peek(2))
193+
self.assertRaises(IndexError, lambda: s_trio.peek(3))
194+
195+
s_trio.push(1, 99)
196+
self.assertEqual(s_trio.peek(1), 99)
197+
s_trio.push(1, 100)
198+
self.assertEqual(s_trio.peek(1), 100)
199+
s_trio.push(1, 101)
200+
self.assertEqual(s_trio.peek(1), 101)
201+
202+
# test that peek still works after popping
203+
val = s_trio.pop(1)
204+
self.assertEqual(val, 101)
205+
self.assertEqual(s_trio.peek(1), 100)
206+
return
207+
208+
def test_stack_pop(self):
209+
# first case, attempt to pop an empty stack
210+
s_trio = StackTrio()
211+
self.assertRaises(IndexError, lambda: s_trio.pop(1))
212+
self.assertRaises(IndexError, lambda: s_trio.pop(2))
213+
self.assertRaises(IndexError, lambda: s_trio.pop(3))
214+
215+
# next, test out pop for each stack
216+
s_trio.push(1, 199)
217+
s_trio.push(2, 299)
218+
s_trio.push(3, 399)
219+
220+
self.assertEqual(s_trio.get_size(1), 1)
221+
self.assertEqual(s_trio.get_size(2), 1)
222+
self.assertEqual(s_trio.get_size(3), 1)
223+
224+
val = s_trio.pop(1)
225+
self.assertEqual(val, 199)
226+
self.assertEqual(s_trio.get_size(1), 0)
227+
228+
s_trio.push(1, 199)
229+
s_trio.push(1, 200)
230+
231+
val = s_trio.pop(1)
232+
self.assertEqual(val, 200)
233+
self.assertEqual(s_trio.get_size(1), 1)
234+
235+
val = s_trio.pop(2)
236+
self.assertEqual(val, 299)
237+
self.assertEqual(s_trio.get_size(2), 0)
238+
239+
s_trio.push(2, 299)
240+
s_trio.push(2, 300)
241+
242+
val = s_trio.pop(2)
243+
244+
self.assertEqual(val, 300)
245+
self.assertEqual(s_trio.get_size(2), 1)
246+
247+
val = s_trio.pop(3)
248+
249+
self.assertEqual(val, 399)
250+
self.assertEqual(s_trio.get_size(3), 0)
251+
252+
s_trio.push(3, 399)
253+
s_trio.push(3, 400)
254+
255+
self.assertEqual(s_trio.get_size(3), 2)
256+
257+
val = s_trio.pop(3)
258+
259+
self.assertEqual(val, 400)
260+
self.assertEqual(s_trio.get_size(3), 1)
261+
262+
def test_validate_stack_id(self):
263+
s_trio = StackTrio()
264+
self.assertRaises(IndexError, lambda: s_trio._validate_stack_id(4))
265+
self.assertRaises(IndexError, lambda: s_trio._validate_stack_id(0))
266+
267+
268+
if __name__ == '__main__':
269+
unittest.main()

0 commit comments

Comments
 (0)