Ch 8: Recursion and Dynamic Programming Flashcards

1
Q

Recursion and Dynamic Programming:
Important Concepts

A
  • When Recursion applies
  • Common Recursive Structures and Runtimes
  • Call Trees
  • Approaches
    • Bottom-Up
    • Top-Down
    • Half and Half
  • Recursive vs Iterative tradeoffs
  • Dynamic Programming
  • Memoization
  • Fibonacci Number Generation
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Signs that a problem has a recursive solution

A
  • It can be built off of subproblems
    • Split data set multiple times
    • Use (n-1)th solution to get nth solution, etc
  • Problem Statements:
    • “Compute the nth ….”
    • “List the first n…”
    • “Compute all…”
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Common Recursive Problem Structures

A
  • Solve f(n) by using f(n-1)
  • Solve problem for first half of data set, then the second half, then merge those results
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Three Common Approaches to Developing a Recursive Algorithm

A
  • Bottom-Up Approach
  • Top-Down Approach
  • Half and Half Approach
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Recursive Algorithms:
Bottom-Up Approach

A
  • Often the most intuitive
  • Start with knowing how to solve in a simple case
    • eg: sort a list with one element
  • Figure out how to solve for 2 elements, then 3, etc
  • Build the solution for one case off of previous cases
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Recursive Algorithms:
Top-Down Approach

A
  • Determine how to divide the problem for case N into subproblems
  • Be careful of overlap between the cases
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Recursive Algorithms:
Half and Half Approach

A
  • Divide the data set in half
    Examples:
  • Binary Search
  • Merge Sort
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Recursive vs Iterative Solutions

A
  • Recursive Algorithms can be very space inefficient
    • Each recursive call adds a layer to the stack
    • recursive depth n -> uses at least O(n) memory
  • All recursive algorithms CAN be implemented iteratively
    • This is often more complex, though
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Dynamic Programming:
Basic Idea

A
  • Take a recursive algorithm
  • Find the overlapping subproblems
    • Subproblems that would be solved over and over with normal recursion
  • Cache the results of subproblems for use in future recursive calls
  • Can also study the pattern of recursive calls and implement something iterative, still caching previous work
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Dynamic Programming
vs
Memoization

A
  • Memoization is “Top-Down” Dynamic Programming
  • Some people only use “Dynamic Programming” to refer to “Bottom-Up” work, with Memoization as a distinct concept
  • This book refers to both as Dynamic Programming
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Dynamic Programming:
Simplest Example

A

Compute the nth Fibonacci number

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Dynamic Programming:
Basic Strategy

A
  • Implement a normal recursive solution
  • Identify if overlapping problems exist
  • Modify solution to add caching
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Fibonacci Numbers(Sequence)

A

A series of numbers, starting with 0 and 1.
Each Fibonacci Number is the sum of the two previous Fibonacci Numbers

F(0) = 0, F(1) = 1
F(n) = F(n-1) + F(n-2) for n > 1

This is one of the most common and simplest examples of Dynamic Programming

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Fibonacci Sequence:
Basic Recursive Solution
and
Runtime

A

int fibonacci(int i) {
if (i == 0) return 0;
if (i == 1) return 1;
return fibonacci(i -1) + fibonacci(i - 2);
}

The runtime may appear at first glance to be O(n) or O(n^2).
Look at the call tree.
* The total number of nodes in the tree represents the runtime, each only does O(1) work outside of it’s recursive calls.
* All of the leaves on the tree are fib(1) and fib(0) (The base cases).
* Every other node has 2 children
* doing this n times gives roughly O(2^n) nodes/runtime
- Actually closer to O(1.6^n), because right subtrees are always one depth smaller. Still an exponential runtime, though.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Fibonacci Sequence:
Memoization Solution and Runtime

A

Start at n, recurse down to base cases and cache solutions.

int fibonacci(int n) {
return fibonacci(n, new int[n + 1]);
}

int fibonacci(int i, int[] memo) {
if (i == 0 I I i == 1) return i;
if (memo[i] == 0) {
memo[i] = fibonacci(i - 1, memo)+ fibonacci(i - 2, memo);
}
return memo[i];
}

Call Tree:
* There is only one branch that goes all the way down to fib(1) and fib(0)
* Other branches reference the cached earlier calls for O(1) call time
* There are roughly 2n nodes, for an O(n) runtime

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Fibonacci Sequence:
Dynamic Programming (Bottom-Up) Solution

A

Compute fib(0) and fib(1), then computer fib(2), fib(3)…up to fib(n)
caching results.

This works better as an iterative solution.
Iterates through n numbers -> Runtime O(n)

17
Q

Fibonacci Sequence:
Improved Dynamic Programming Solution

A

With the “bottom up” approach, to calculate memo[i], only memo[i-1] and memo[i-2] are needed. Everything from memo[i-3] and below can be discarded to save space. Really only need three variables.