Lab 7: Recursion
Creating the necessary folder
Make sure to create a subfolder called
lab7 within your cs112 folder, and put all of the files for this
lab in that folder.
Task 1: Recursion, the basics
In class we’ve begun talking about how recursion works, how to understand recursive methods, and how to trace them. With lab we’ll be talking a bit about how to design recursive methods – how to break down problems to fit the recursive mold.
-
Begin by downloading the following starter file to your
lab7folder:RecurCount.java -
In VS Code, open your
lab7folder using File->Open Folder, and then click on the name of the downloaded file in the Explorer Pane to open an editor window for it. -
Count the number of times that a specific number occurs in an array of integers. To do so, you should write a recursive method with the following header:
public static int countN(int n, int[] arr, int index) {
It should determine the number of times that the integer
nappears in the portion of the array ofarrthat goes from the specifiedindexto the end of the array.For example, if we have:
int[] a = {5, 3, 5, 2, 7, 3, 5};
then:
-
countN(5, a, 0)should return3, because there are 3 occurrences of the integer5in the entire array (i.e., the portion of the array that goes from index 0 to the end of the array). -
countN(5, a, 1)should return2, because there are 2 occurrences of the integer5in the portion of the array that goes from index 1 to the end of the array. -
countN(5, a, 3)should return1, because there is 1 occurrence of the integer5in the portion of the array that goes from index 3 to the end of the array.
Note that the inclusion of the
indexparameter is what will allow you to reduce the problem when you make the recursive call! -
Task 2: Tracing a method that makes multiple recursive calls
A number of the algorithms that we consider in CS 112 involve recursive methods in which a given invocation of the method may make two or more recursive calls. To understand how this type of algorithm works, it helps to use a diagram known as a call tree.
Let’s work together to develop a call tree for the execution of the following recursive method. (The method allows us to recursively generate the nth integer in the Fibonacci sequence, although you don’t need to be familiar with that sequence to understand this problem.)
public static int fib(int n) { if (n == 0 || n == 1) { return 1; } else { int prev1 = fib(n - 2); int prev2 = fib(n - 1); return prev1 + prev2; } }
Note: We are using a slightly modified version of the Fibonacci sequence that begins with 1 instead of 0.
Assume that we begin with the following initial call to this method:
fib(4)
-
Let’s draw a call tree for the sequence of calls that are involved in computing the result of
fib(4). As we do so, we’ll number each call of the method in the order in which they are made. -
The order in which the calls are made is not the same as the order in which the calls return. A given invocation of the method can’t return until both of the calls that it makes (
fib(n - 2)andfib(n - 1)) return.Underneath your call tree, list the order in which the calls return, as well as their return values. When you refer to a given call in this part of the problem, use its number from the call tree.
For example, the initial call always returns last, so the last line in this part of the problem should look like this:
call 1 (fib(4)) returns ...
where you replace the
...with its return value. -
Re-write the recursive
fibmethod so that it has only onereturnstatement at the end of the method. Hint: Use a variable for the return value, and initialize that variable to the value that would be returned in the base case. Then modify that variable as needed by making the recursive calls.Does the revised version of the method work the same way?
Task 3: Recursive Backtracking
In lecture we saw how we could use the technique of recursive backtracking to solve the N-Queens problem. We can use the same technique for determining how to color states on a map.
What are the common characteristics of these two problems?
Understanding how recursive backtracking works in these two scenarios will help you implement a recursive backtracking solution for the next problem set.
Task 4: Practice algorithm analysis
Consider the following code fragment:
for (int i = 0; i < n; i++) { for (int j = 0; j < 2*n; j++) { methodA(); for (int k = 0; k < n + 1; k++) { methodB(); } } for (int j = 0; j < i; j++) { methodC(); } if (i % 2 == 0) { methodD(); } }
For each of the following, find the function that expresses the
number of times each part of the code is executed for a given
value of n, and then determine the corresponding big-O expression.
- the number of times
methodA()is called - the number of times
methodB()is called - the number of times
methodC()is called - the number of times
methodD()is called
Task 5: More practice with recursive methods
Write a recursive method to solve each of the following:
-
Find the product of the integers from 1 through
n(this is called the factorial function). Write a recursive method that takes in an integernand computes (and returns) the factorial of that integer. Ifnis zero, return 1.What happens if you try to compute the factorial of a large number? Why do you think this is the case? How could we accommodate larger results?
-
Find the minimum element in an array of integers. Write a recursive method that takes in an array of integers and whatever other parameters are needed, and that returns the smallest integer in the array.
Hint: You should use an additional parameter to keep track of what portion of the array the current call is focused on, just as we did in the previous problem.
-
Challenge: The greatest common denominator is a common problem in mathematics. Given two positive numbers, what is the largest factor that divised both integers? Write a recursive method that takes in two integers and finds their greatest common denominator. (Hint: Look up Euclid’s algorithm online.)