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