1
+ """Python version 3.7.0
2
+ 3.4 - Queue via Stacks:
3
+ Implement a MyQueue class which implements a queue using two stacks.
4
+ """
5
+ import copy
6
+ import unittest
7
+
8
+ from dataclasses import dataclass
9
+ from typing import Generic , TypeVar
10
+ from typing import List , Optional , Iterator
11
+
12
+ T = TypeVar ('T' )
13
+
14
+ @dataclass
15
+ class StackNode (Generic [T ]):
16
+ data : T
17
+ next : 'Optional[StackNode[T]]'
18
+
19
+
20
+ class MyStack (Generic [T ]):
21
+ """Stack data structure implementation.
22
+ Uses LIFO (last-in first-out) ordering.
23
+ The most recent item added to the stack is
24
+ the first removed. Traversal is top to bottom.
25
+ """
26
+ def __init__ (self ):
27
+ self .top : Optional [StackNode [T ]] = None # top is a pointer to StackNode object
28
+ self ._size : int = 0
29
+ self .index : int = - 1 # tracking current node for iterator use
30
+ self .current_node : Optional [StackNode [T ]] = self .top
31
+ return
32
+
33
+ def pop (self ) -> T :
34
+ """
35
+ Removes the top item from the stack
36
+ Raises:
37
+ IndexError: raised when pop is attempted on empty stack
38
+ Returns:
39
+ int: The data at the top of the stack
40
+ """
41
+ if self .top is None :
42
+ raise IndexError ('Stack is Empty.' )
43
+ item = self .top .data
44
+ self .top = self .top .next
45
+ self .current_node = self .top
46
+ self ._size -= 1
47
+ return item
48
+
49
+ def push (self , item : T ) -> None :
50
+ """
51
+ Adds an item to the top of the stack
52
+ Args:
53
+ item (int): data we want at the top of stack
54
+ """
55
+ t = StackNode (item , None )
56
+ t .next = self .top
57
+ self .top = t
58
+ self .current_node = self .top
59
+ self ._size += 1
60
+
61
+ def peek (self ) -> T :
62
+ """
63
+ Returns data at the top of the stack
64
+ Raises:
65
+ IndexError: [description]
66
+ Returns:
67
+ int: the value at the top of the stack
68
+ """
69
+ if self .top is None :
70
+ raise IndexError ('Stack is Empty' )
71
+ return self .top .data
72
+
73
+ def empty (self ) -> None :
74
+ while self ._size > 0 :
75
+ self .pop ()
76
+
77
+ def __iter__ (self ) -> Iterator :
78
+ """
79
+ Builds a list of the current stack state.
80
+ For example, given the following stack:
81
+ 3 -> 2 -> 1, where 3 is the top,
82
+ Expect:
83
+ [3, 2, 1]
84
+ Returns:
85
+ List[int]: list of integers
86
+ """
87
+ return self
88
+
89
+ def __next__ (self ) -> T :
90
+ self .index += 1
91
+ if self .index == self ._size or self .current_node is None :
92
+ self .index = - 1
93
+ self .current_node = self .top
94
+ raise StopIteration
95
+ n : T = self .current_node .data
96
+ self .current_node = self .current_node .next
97
+ return n
98
+
99
+ def __bool__ (self ) -> bool :
100
+ """
101
+ True is returned when the container is not empty.
102
+ From https://docs.python.org/3/reference/datamodel.html#object.__bool__ :
103
+ Called to implement truth value testing and the built-in operation bool();
104
+ should return False or True. When this method is not defined, len() is called,
105
+ if it is defined, and the object is considered true if its result is nonzero.
106
+ If a class defines neither len() nor bool(), all its instances are considered true.
107
+ Returns:
108
+ bool: False when empty, True otherwise
109
+ """
110
+ return self ._size > 0
111
+
112
+ def __len__ (self ) -> int :
113
+ return self ._size
114
+
115
+ def __str__ (self ):
116
+ if self ._size == 0 :
117
+ return '<Empty>'
118
+ values = []
119
+ n = self .top
120
+ while n .next is not None :
121
+ values .append (str (n .data ))
122
+ n = n .next
123
+ values .append (str (n .data ))
124
+ return '->' .join (values )
125
+
126
+
127
+ @dataclass
128
+ class QueueNode (Generic [T ]):
129
+ data : T
130
+ next : 'Optional[QueueNode[T]]'
131
+
132
+
133
+ class MyQueue (Generic [T ]):
134
+ def __init__ (self ):
135
+ self ._size = 0
136
+ self .data_stack = MyStack [T ]() # will be used as main data container
137
+
138
+ def add (self , item : T ):
139
+ self .data_stack .push (item )
140
+ self ._size += 1
141
+
142
+ def _produce_reversed_stack (self , input_stack : MyStack ) -> MyStack :
143
+ """This function will take in a stack,
144
+ and return a copy of a reversed version of that
145
+ stack.
146
+
147
+ Args:
148
+ input_stack (MyStack): Stack of type T
149
+ """
150
+ temp_stack = MyStack [T ]()
151
+ while len (input_stack ) > 0 :
152
+ d = input_stack .pop ()
153
+ temp_stack .push (d )
154
+ return temp_stack
155
+
156
+ def _pop (self ) -> T :
157
+ # 1. local temp stack
158
+ temp_stack : MyStack = self ._produce_reversed_stack (self .data_stack )
159
+ # 2. extract value at top of 2nd stack.
160
+ data : T = temp_stack .pop ()
161
+ # 3. move remaining values back to stack one
162
+ self .data_stack = self ._produce_reversed_stack (temp_stack )
163
+ return data
164
+
165
+ def remove (self ) -> T :
166
+ if self ._size == 0 :
167
+ raise Exception ('No values in stack to remove' )
168
+ # use second stack to extract item of interest
169
+ data : T = self ._pop ()
170
+ self ._size -= 1
171
+ return data
172
+
173
+ def _peek (self ) -> T :
174
+ # 1. local temp stack
175
+ temp_stack : MyStack = self ._produce_reversed_stack (self .data_stack )
176
+ # 2. extract value at top of 2nd stack.
177
+ data : T = temp_stack .peek ()
178
+ # 3. move remaining values back to stack one
179
+ self .data_stack = self ._produce_reversed_stack (temp_stack )
180
+ return data
181
+
182
+ def peek (self ) -> T :
183
+ if self ._size == 0 :
184
+ raise Exception ('No values in stack to remove' )
185
+ return self ._peek ()
186
+
187
+ def is_empty (self ) -> bool :
188
+ return self ._size == 0
189
+
190
+ def __len__ (self ) -> int :
191
+ return self ._size
192
+
193
+ def __bool__ (self ) -> bool :
194
+ """
195
+ True is returned when the container is not empty.
196
+ From https://docs.python.org/3/reference/datamodel.html#object.__bool__ :
197
+ Called to implement truth value testing and the built-in operation bool();
198
+ should return False or True. When this method is not defined, len() is called,
199
+ if it is defined, and the object is considered true if its result is nonzero.
200
+ If a class defines neither len() nor bool(), all its instances are considered true.
201
+ Returns:
202
+ bool: False when empty, True otherwise
203
+ """
204
+ return self ._size > 0
205
+
206
+
207
+ class TestMyQueue (unittest .TestCase ):
208
+ def test_add (self ):
209
+ q = MyQueue ()
210
+ self .assertEqual (len (q ), 0 )
211
+ # going to add 1, 3, 5.
212
+ # FIFO, so when removing, 1 goes out first
213
+ q .add (1 )
214
+ self .assertEqual (len (q ), 1 )
215
+ self .assertEqual (q .peek (), 1 )
216
+ q .add (3 )
217
+ self .assertEqual (len (q ), 2 )
218
+ self .assertEqual (q .peek (), 1 )
219
+ q .add (5 )
220
+ self .assertEqual (len (q ), 3 )
221
+ self .assertEqual (q .peek (), 1 )
222
+
223
+ def test_remove (self ):
224
+ q = MyQueue ()
225
+ q .add (1 )
226
+ q .add (2 )
227
+ q .add (3 )
228
+ val = q .remove ()
229
+ self .assertEqual (val , 1 )
230
+ val = q .remove ()
231
+ self .assertEqual (val , 2 )
232
+ val = q .remove ()
233
+ self .assertEqual (val , 3 )
234
+ self .assertRaises (Exception , lambda : q .remove ())
235
+
236
+ def test_peek (self ):
237
+ q = MyQueue ()
238
+ self .assertRaises (Exception , lambda : q .peek ())
239
+ q .add (99 )
240
+ q .add (45 )
241
+ self .assertEqual (q .peek (), 99 )
242
+
243
+ def test_is_empty (self ):
244
+ q = MyQueue ()
245
+ self .assertTrue (q .is_empty ())
246
+ self .assertFalse (q )
247
+ q .add (100 )
248
+ self .assertFalse (q .is_empty ())
249
+ self .assertTrue (q )
250
+
251
+
252
+ class TestMyStack (unittest .TestCase ):
253
+ def test_stack_push (self ):
254
+ s = MyStack ()
255
+ self .assertEqual (len (s ), 0 )
256
+ self .assertEqual (s .top , None )
257
+ s .push (2 )
258
+ self .assertEqual (len (s ), 1 )
259
+ self .assertEqual (s .top .data , 2 )
260
+ self .assertEqual (s .top .next , None )
261
+ s .push (3 )
262
+ self .assertEqual (len (s ), 2 )
263
+ self .assertEqual (s .top .data , 3 )
264
+ self .assertEqual (s .top .next .data , 2 )
265
+ s .push (4 )
266
+ self .assertEqual (len (s ), 3 )
267
+ self .assertEqual (s .top .data , 4 )
268
+ self .assertEqual (s .top .next .data , 3 )
269
+ l = list (s )
270
+ self .assertEqual (l , [4 , 3 , 2 ])
271
+
272
+ def test_stack_peek (self ):
273
+ s = MyStack ()
274
+ with self .assertRaises (IndexError ):
275
+ s .peek ()
276
+ s .push (1 )
277
+ s .push (2 )
278
+ s .push (99 )
279
+ top_val = s .peek ()
280
+ self .assertEqual (top_val , 99 )
281
+
282
+ def test_stack_pop (self ):
283
+ # first case, attempt to pop an empty stack
284
+ s = MyStack ()
285
+ with self .assertRaises (IndexError ):
286
+ s .pop ()
287
+ s .push (1 )
288
+ s .push (2 )
289
+ s .push (3 )
290
+ # size is 3
291
+ self .assertEqual (list (s ), [3 , 2 , 1 ])
292
+ val = s .pop ()
293
+ self .assertEqual (val , 3 )
294
+ self .assertEqual (len (s ), 2 ) # size should now be 2
295
+ self .assertEqual (list (s ), [2 , 1 ])
296
+
297
+ def test__bool__ (self ):
298
+ s = MyStack ()
299
+ self .assertFalse (s )
300
+ s .push (3 )
301
+ self .assertTrue (s )
302
+
303
+
304
+ if __name__ == '__main__' :
305
+ unittest .main ()
0 commit comments