Week4 Worksheets
Week4 Worksheets
We are interested in writing client code and need a function (outside the class) to determine the number of items on a stack.
1. Identify what is wrong with each of the following 4 implementations of the size function.
>>> s = Stack()
>>> size(s)
0
>>> s.push('hi')
>>> s.push('more')
>>> s.push('stuff')
>>> size(s)
3
"""
count = 0
for _ in s:
count += 1 Not necessary interable
return count
class Stack:
"""A last-in-first-out (LIFO) stack of items.
Stores data in first-in, last-out order. When removing an item from the
stack, the most recently-added item is the one that is removed.
Attributes:
- _items: The items stored in the stack. The end of the list represents the top of the stack.
"""
_items: list
1. We are going to revise the code so that instead of putting the top of the stack at the end of the list, the top of the stack
is at the beginning of the list.
(a) What code has to change?
push and pop!
3. With these changes to the Stack class, what changes would be required to client code that was already using the Stack
class? Explain.
None! Our public interface stayed exactly the same, so it’s still work as intended.
We can switch implementations whenever we want (a benefit of abstraction)!
4. Which implementation do you think is better? What criteria are you using?
1. Answer the following questions to make sure you understand the key concepts before moving on.
(b) Suppose we have a list of length n. If we want to insert a new item at position i, how many list elements must be
moved? (Be careful about o↵-by-one errors!)
n-i i = 0 : all items moved; i = 1 : n-1 items moved, etc
(c) Suppose we have a list of length n. If we want to remove the existing item at position i, how many list elements must
be moved? Do not include the item being removed.
n-i-1 i = 0: n-1 items moved; i = 1: n-2 times moved, etc…
(d) Suppose we have two lists: one of length 100, and one of length 1,000,000. Give an example of each of the following:
(i) An operation that would be faster on the smaller list than the larger list.
insert/ remove at front
(ii) An operation that would take roughly the same amount of time on the two lists.
list.pop() insert/remove at the end, indexing reassigning
(e) Suppose we implement a Queue using a Python list, where the front of the list represents the front of the queue.
Which operation do we expect to take longer as the queue size increases: Queue.enqueue or Queue.dequeue?
Queue.dequeue dequeue: remove from the front of the list
2. Now let’s look at some code. Suppose we have two implementations of the Stack ADT: StackA has push and pop operations
that take 1 step, regardless of stack size, while StackB has push and pop operations that take n + 1 steps, where n is the
number of items currently on the stack. (You might argue it’s “n steps”, but that di↵erence doesn’t matter here.)
For each of the code snippets on the next page, calculate the number of steps taken in total by all push and pop operations
that are performed by the code. Do each calculation twice: once assuming we use the StackA implementation, and once
assuming we use the StackB implementation. Ignore all other operations for this exercise—you’re only counting steps for
push and pop here.
(a) # s starts as a stack of size n
s.push(1)
s.pop()
StackA StackB
push: 1 step push: n+1 steps. stack of size n
pop: 1 step pop: n + 1+ 1 steps stack of size n + 1
= 2 steps in total total = 2x + 3
(c) # s starts as an empty stack, k is a positive integer. Calculate the number of steps in terms of k.
# Hint: 1 + 2 + 3 + ... + k = k * (k + 1) / 2
for i in range(k)
s.push(i)
StackA StackB
1 step
1 step * k 2 steps
in total = k steps …
…
k steps
in total = 1 + … + k = k*(k+1)/2
StackA StackB
First Loop:
First Loop:
within 1 loop:
n+1 step + 1 step
1 step + 1 step
n steps + 2 step
1 step + 1step
…
1 step + 1 step
…
…
2 steps +n steps
…
1 step + n step
intotal = n*(n+1) + n
In total: n steps + n steps = 2n steps
Second Loop:
same
Second Loop:
1 step + 1 step in total 2n*(n+1) + 2n
…
in total = 2n steps
in total 4n