0% found this document useful (0 votes)
8 views4 pages

Week4 Worksheets

The document discusses the implementation of a size function for a Stack class, identifying issues with four incorrect implementations. It also outlines a revised Stack class where the top of the stack is at the beginning of the list, emphasizing that public interfaces remain unchanged despite implementation modifications. Additionally, it covers running time efficiency for list operations and compares two Stack implementations based on their performance in push and pop operations.

Uploaded by

jiwei Zhang
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views4 pages

Week4 Worksheets

The document discusses the implementation of a size function for a Stack class, identifying issues with four incorrect implementations. It also outlines a revised Stack class where the top of the stack is at the beginning of the list, emphasizing that public interfaces remain unchanged despite implementation modifications. Additionally, it covers running time efficiency for list operations and compares two Stack implementations based on their performance in push and pop operations.

Uploaded by

jiwei Zhang
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 4

CSC148 - A client function for class Stack: size

Recall the Stack code, which contains the following:

• a private attribute items: list


• public methods init , push, pop, and is empty

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.

def size(s: Stack) -> int:


"""Return the number of items in s.

>>> 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

def size(s: Stack) -> int:


"""Return the number of items in s.
"""
count = 0
while not s.is_empty():
s.pop() Change original s although
count += 1 count will return the desired value
return count

def size(s: Stack) -> int:


"""Return the number of items in s.
"""
return len(s._items) cannot access privative attributes

def size(s: Stack) -> int:


"""Return the number of items in s.
"""
s_copy = s
count = 0
while not s_copy.is_empty(): Change original s, it’s an
s_copy.pop() alias(another name but
count += 1
same object ) not a copy
return count
so s is still changed
2. Given what you’ve learned, implement the function yourself.
def size(s: Stack) -> int:
“”” Return the number of items in s “””
s
CSC148 - Considering a di↵erent implementation of class Stack
Below is the complete stack class that you saw in the readings (with docstrings condensed for space).

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

def __init__(self) -> None:


"""Initialize a new empty stack."""
self._items = []

def is_empty(self) -> bool:


"""Return whether this stack contains no items."""
return self._items == []

def push(self, item: Any) -> None:


"""Add a new element to the top of this stack."""
self._items.append(item)

def pop(self) -> Any:


"""Remove and return the element at the top of this stack."""
return self._items.pop()

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!

(b) Make the changes.


2. What docstrings must change to go along with your code changes? Explain.
None(or nothing public)! We never mention implementation details
anywhere, so we don’t have to update anything, since it’s abstract

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?

Efficiency: Our original was faster, No need to shift intems in memory


REadbility: Both are equal
FOr the client code: No difference, public interface won’t change
CSC148 - Running time efficiency: Lists, Stacks, and Queues
We have now seen that Python lists have the following running times for key operations:

• Accessing or assigning to any element by index takes constant time.


• Inserting or removing an item at a given index takes time proportional to the number of items after the index.

1. Answer the following questions to make sure you understand the key concepts before moving on.

(a) What do we mean by “constant time” above?


it takes the same amount of time regardless of the number of items we have (“input size”)

(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

(b) # s starts as an empty stack


for i in range(5)
s.push(i)

StackA StackB 1 step


1step * 5 2 steps
in total = 5 steps 3 steps
4 steps
5 steps
in total = 15 steps

(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

(d) # s1 starts as a stack of size n, and s2 starts as an empty stack


# Hint 1: it can help to count the steps per iteration of the loop rather than considering s1 and s2 separately
# Hint 2: the loops are the same, but with variables s1 and s2 swapped
while not s1.is_empty():
s2.push(s1.pop())

while not s2.is_empty():


s1.push(s2.pop())

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

You might also like