week 5 - monday. what did we talk about last time? linked list implementations stacks queues
TRANSCRIPT
CS221Week 5 - Monday
Last time
What did we talk about last time? Linked list implementations
Stacks Queues
Questions?
Infix to Postfix Converter
Project 2
Assignment 2 Post-Mortem
Project 1 loops
For some reason, people like to have crazy loops:
For this (relatively simple) case, it's not terrible, but the more complex it gets, the harder it is to think about indexes that jump around
Even so, what do i and j mean in this case?
for( int i = 0; i < height; ++i )for( int j = 0; j < width*3; j += 3 ) {
data[i][j] = 255 – data[i][j]);data[i][j + 1] = 255 – data[i][j + 1];data[i][j + 2] = 255 – data[i][j + 2];
}
Project 1 loops continued I like to keep my for loop indexes simple,
because loops are hard to thinking about Written this way, my indexes are rows and
columns:for( int row = 0; row < height; ++row )
for( int column = 0; column < width; ++column ) {data[row][3*column] =
255 – data[row][3*column]);data[row][3*column + 1] =
255 – [row][3*column + 1];data[row][3*column + 2] =
255 – [row][3*column + 2];}
Project 1 loops continued
Or even better:
I don't object to i and j, but a loop index should mean something
for( int row = 0; row < height; ++row )for( int column = 0; column < width; ++column )
for( int color = 0; color < 3; ++color )data[row][3*column + color] = 255 – data[row][3*column + color]);
Recursion
What is Recursion?
Defining something in terms of itself
To be useful, the definition must be based on progressively simpler definitions of the thing being defined
Bottom Up
It is possible to define something recursively from the bottom up
We start with a simple pattern and repeat the pattern, using a copy of the pattern for each part of the starting pattern
Top Down
Explicitly: n! = (n)(n – 1)(n – 2) … (2)(1)Recursively: n! = (n)(n – 1)! 6! = 6 ∙ 5!
5! = 5 ∙ 4!▪ 4! = 4 ∙ 3!▪ 3! = 3 ∙ 2!
2! = 2 ∙ 1! 1! = 1
6! = 6 ∙ 5 ∙ 4 ∙ 3 ∙ 2 ∙ 1 = 720
Examples in Acronyms
PHP PHP: Hypertext Processor▪ (PHP: Hypertext Processor): Hypertext
Processor▪ …
XINU XINU Is Not Unix▪ (XINU Is Not Unix) Is Not Unix▪ …
Useful Recursion
Two parts: Base case(s)
Tells recursion when to stop For factorial, n = 1 or n = 0 are
examples of base cases Recursive case(s)
Allows recursion to progress "Leap of faith" For factorial, n > 1 is the recursive case
Solving Problems with Recursion
Approach for Problems
Top down approach Don’t try to solve the whole problem Deal with the next step in the
problem Then make the "leap of faith" Assume that you can solve any
smaller part of the problem
Problem
ProblemProblemProblem
Walking to the Door
Problem: You want to walk to the door
Base case (if you reach the door): You’re done!
Recursive case (if you aren’t there yet): Take a step toward the door
Problem
Implementing Factorial
Base case (n 1): 1! = 0! = 1
Recursive case (n > 1): n! = n(n – 1)!
Code for Factorial
public static long factorial( int n ){if( n <= 1 )
return 1;else
return n*factorial( n – 1 );}
Base Case
RecursiveCase
Count the Zeroes
Given an integer, count the number of zeroes in its representation
Example: 13007804 3 zeroes
Recursion for Zeroes
Base cases (number less than 10): 1 zero if it is 0 No zeroes otherwise
Recursive cases (number greater than or equal to 10): One more zero than the rest of the
number if the last digit is 0 The same number of zeroes as the rest
of the number if the last digit is not 0
Code for Zeroes
public static int zeroes( int n ){if( n == 0 )
return 1;else if( n < 10 )
return 0;else if( n % 10 == 0 )
return 1 + zeroes( n / 10 );else
return zeroes( n / 10 );}
Base Cases Recursive
Cases
Searching in a Sorted Array
Given an array of integers in (ascending) sorted order, find the index of the one you are looking for
Useful problem with practical applications
Recursion makes an efficient solution obvious
Play the High-Low game
Recursion for Binary Search Base cases:
The number in the middle of the range is the one you are looking for. Return its index.
The number isn’t in the range you are looking at. Return -1.
Recursion cases: The number in middle of the range is too
high. Look in the range below it. The number in the middle of the range is too
low. Look in the range above it.
Code for Binary Search
public static int search( int[] array, int n, int start, int end )
{int midpoint = (start + end)/2;if( array[midpoint] == n )
return midpoint;else if( start >= end )
return -1;else if( array[midpoint] < n )
return search( array, n, midpoint + 1, end );
elsereturn search( array, n, start,
midpoint );}
BaseCases
RecursiveCases
Time for Binary Search
Each recursive call splits the range in half In the worst case, we will have to keep
splitting the range in half until we have a single number left
We want to find the number of times that we have to multiply n by ½ before we get 1 n(½)x = 1 n = 2x
x = log2(n)
How Does it Work Inside The Computer?Pay no attention to the man behind the curtain…
All this math is great, but…
How does it actually work inside a computer?
Is there a problem with calling a function inside the same function?
How does the computer keep track of which function is which?
The Stack
As you know, a stack is a FILO data structure used to store and retrieve items in a particular order
Just like a stack of blocks:
A A
BPush
A
B
CPush
A
BPop
Stack for Functions
In the same way, the local variables for each function are stored on the stack
When a function is called, a copy of that function is pushed onto the stack
When a function returns, that copy of the function pops off the stack
main main
solve
Call
main
solve
factorial
Call
main
solve
Return
Example with Factorial
Each copy of factorial has a value of n stored as a local variable
For 6! :
6*factorial(5)
5*factorial(4)
4*factorial(3)
3*factorial(2)
2*factorial(1)
1
x = factorial(6);factorial(6)
factorial(5)
factorial(4)
factorial(3)
factorial(2)
factorial(1)
720
120
24
6
2
1
Issues of Efficiency
When to use recursion?
Recursion is a great technique One of its strengths is in writing
concise code to solve a problem Some recursive solutions are very
efficient Some are not It pays to be aware of both
Summation
Find the sum of the integers 1 through n
Example: n = 8 sum(8) = 8 + 7 + 6 + 5 + 4 + 3 + 2
+ 1 sum(8) = 36
Recursion for Summing
Base case (n = 1):
Recursive case (n > 1):
1
1
1i
i
1
11
n
i
n
i
ini
Code for Summing
public static int sum( int n ){if( n == 1 )
return 1;else
return n + sum( n – 1 );}
Base Case
RecursiveCase
Why not recursion?
Recursive summing takes linear time (summing n takes n function calls)
Is there another way to find this sum?
Closed form equation
Constant time! Remember the story of young Gauss
n
i
nni
1 2)1(
Fibonacci
The sequence: 1 1 2 3 5 8 13 21 34 55… Studied by Leonardo of Pisa to model the
growth of rabbit populations
Fibonacci Problem
Find the nth term of the Fibonacci sequence
Simple approach of summing two previous terms together
Example: n = 71 1 2 3 5 8 131 2 3 4 5 6 7
Recursion for Fibonacci
Base cases (n = 1 and n = 2): Result = 1
Recursive case (n > 2): Result = fibonacci(n – 1) + fibonacci(n –
2)
Code for Fibonacci
public static int fib( int n ){if( n <= 2 )
return 1;else
return fib(n – 1) + fib(n – 2);
}
Base Case
RecursiveCase
What’s the running time for fib()?
Example: fib(6) fib(6)
fib(4)fib(5)
fib(4) fib(3)
fib(3) fib(2) fib(2) fib(1)
fib(2) fib(1)
fib(3) fib(2)
fib(2) fib(1)
Uh oh.
Exponential Time for fib
For most cases, calling fib() makes calls two more calls to fib(), which each make two more calls to fib(), and so on…
Many values are redundantly computed
The final running time is O(2n/2)
Can we do better?
The recursion is fine from a mathematical perspective
We just need to avoid recomputing lower terms in the sequence
We can use the idea of carrying along both the (n – 1) term and the (n – 2) term in each recursive step
Code for Better Fibonacci
public static int fib2( int a, int b, int n )
{if( n <= 2 )
return b;else
return fib2(b, a + b, n - 1);}
//proxy methodint fib( int n ){return fib2(1, 1, n);
}
Base Case
RecursiveCase
Exponentiation
We want to raise a number x to a power n, like so: xn
We allow x to be real, but n must be an integer greater than or equal to 0
Example: (4.5)13 = 310286355.9971923828125
Recursion for Exponentiation
Base case (n = 0): Result = 1
Recursive case (n > 0): Result = x ∙ x(n – 1)
Code for Exponentiation
public static double power( double x, int n )
{if( n == 0 )
return 1;else
return x * power( x, n – 1);}
Base Case
RecursiveCase
Running time for power
Each call reduces n by 1n total calls What's the running time?
O(n)
Can we do better?
We need to structure the recursion differently
Instead of reducing n by 1 each time, can we reduce it by a lot more?
It’s true that xn = x ∙ x(n – 1)
But, it is also true that xn = x(n/2) ∙ x(n/2)
New Recursion for Exponentiation
Assume that n is a power of 2 Base case (n = 1):
Result = x Recursive case (n > 1):
Result = (x(n/2))2
Code for Better Exponentiation
public static double power2( double x, int n )
{double temp;if( n == 1 )
return x;else{
temp = power2( x, n/2 );return temp * temp;
}}
Base Case
RecursiveCase
Running time for power2
Each call reduces n by half log2(n) total calls Just like binary search Can we expand the algorithm to
even and odd values of n?
Even Newer Recursion for Exponentiation
Base case (n = 1): Result = x
Recursive cases (n > 1): If n is even, result = (x(n/2))2
If n is odd, result = x ∙ (x((n – 1)/2))2
Code for Even Better Exponentiation
public static double power3( double x, int n ){double temp;if( n == 1 )
return x;else if( n % 2 == 0 ){
temp = power3( x, n/2 );return temp * temp;
}else{
temp = power3( x, (n – 1)/2 );return x * temp * temp;
}}
Base Case
RecursiveCases
Running time for power3
Each call reduces n by half (more or less) O(log2 n) total calls Does as well as power2() Better yet, we can use this solution to get
a logarithmic time answer for Fibonacci!
The nth term of the Fibonacci sequence is:
Where
Upcoming
Next time…
Review for Exam 1
Reminders
Study for Exam 1 This Friday, September 25, in class
Start working on Project 2 Sign up for new teams on Canvas
immediately! You can't partner with someone you've
partnered with before I will start docking points if you haven't
picked your teams by Friday