Download Data Structures: Ordered List, Recursion & Dynamic Programming in CS 1520 Lec 9 and more Study notes Data Structures and Algorithms in PDF only on Docsity!
1. The textbook’s ordered list ADT uses a singly-linked list implementation. I added the _size, _tail, _current,
_previous, and _currentIndex attributes:
data next data next data next data next data next
_head
_currentIndex
_tail
_current
_size
_previous
'a' 'c' 'm' 'w' 'y'
OrderedList Object
a) The search(targetItem) method searches for targetItem in the list. It returns True if targetItem is in the list;
otherwise it returns False. Additionally, it has the side-effects of setting _current, _previous, and
_currentIndex. Complete the search(targetItem) method code:
class OrderedList:
def search(self, targetItem):
b) Complete the add(item) method including a check of it’s precondition(s)?
def add(self, item):
2. A recursive function is one that calls itself. Complete the recursive code for the countDown function that is
passed a starting value and proceeds to count down to zero and prints “Blast Off!!!”.
Hint: The countDown function, like most recursive functions, solves a problem by splitting the problem into one or
more simpler problems of the same type. For example, countDown(10) prints the first value (i.e, 10) and then
solves the simpler problem of counting down from 9. To prevent “infinite recursion”, if-statement(s) are used to check
for trivial base case (s) of the problem that can be solved without recursion. Here, when we reach a countDown(0)
problem we can just print “Blast Off!!!”.
Enter count down start: 10
Count Down: 10 9 8 7 6 5 4 3 2 1
Blast Off!!!
Program Output:
""" File: countDown.py
def main(): start = eval(input("Enter count down start: ")) print( "\nCount Down:") countDown(start)
def countDown(count):
main()
a) Trace the function call countDown(5) on paper by drawing the run-time stack and showing the output.
b) What do you think will happen if your call countDown(-1)?
c) Why is there a limit on the depth of recursion?
c) On my office computer, the call to fib(40) takes 22 seconds, the call to fib(41) takes 35 seconds, and the call to
fib(42) takes 56 seconds. How long would you expect fib(43) to take?
d) How long would you guess calculating fib(100) would take on my office computer?
e) Why do you suppose this recursive fib function is so slow?
f) What is the computational complexity? O ( )
g) How might we speed up the calculation of the Fibonacci series?
4. A VERY POWERFUL concept in Computer Science is dynamic programming. Dynamic programming solutions
eliminate the redundancy of divide-and-conquer algorithms by calculating the solutions to smaller problems first,
storing their answers, and looking up their answers if later needed instead of recalculating them.
We can use a list to store the answers to smaller problems of the Fibonacci sequence.
To transform from the recursive view of the problem to the dynamic programming solution you can do the following
steps:
1) Store the solution to smallest problems (i.e., the base cases) in a list
2) Loop (no recursion) from the base cases up to the biggest problem of interest. On each iteration of the loop we:
solve the next bigger problem by looking up the solution to previously solved smaller problem(s)
store the solution to this next bigger problem for later usage so we never have to recalculate it
a) Complete the dynamic programming code:
def fib(n): """Dynamic programming solution to find the nth number in the Fibonacci seq."""
List to hold the solutions to the smaller problems
fibonacci = []
Step 1: Store base case solutions
fibonacci.append( ) fibonacci.append( )
Step 2: Loop from base cases to biggest problem of interest
for position in range( ):
fibonacci.append( )
return nth number in the Fibonacci sequence
return
Running the above code to calculate fib(100) would only take a fraction of a second.
b) One tradeoff of simple dynamic programming implementations is that they can require more memory since we
store solutions to all smaller problems. Often, we can reduce the amount of storage needed if the next larger problem
(and all the larger problems) don’t really need the solution to the really small problems, but just the larger of the
smaller problems. In fibonacci when calculating the next value in the sequence how many of the previous solutions are
needed?