The document discusses recursion and provides examples of recursive algorithms and data structures including arithmetic series, Fibonacci sequence, binary search, and mergesort. Recursion involves breaking a problem down into smaller sub-problems until they can be solved directly, and combining the solutions to solve the original problem. Examples demonstrate how recursion uses function calls to solve problems by dividing them into base cases and recursively calling itself on smaller instances.
1. Recursion lecture in Java
Recursively breaking down a problem into two or more sub-problems of the
same (or related) type, until these become simple enough to be solved directly.
The solutions to the sub-problems are then combined to give a solution to the
original problem.
Top-Down Design
Cleaning apartment/house.
Example
Divide and Conquer Algorithm
Many divide and conquer type algorithms can be implemented as recursive
functions.
Condition that leads to a recursive method returning without making another
recursive call.
Stops the recursion.
Solution to this case is simple enough to solve directly.
Base case.
Calls itself to solve smaller sub-problems.
May combine the results of the sub-problems.
Properties of recursive functions
Recursive methods
/**
* This is the recursive form of arithmetic series
* x = x + (x-1) + (x-2) + .. + 0
*/
public class ArthmeticSeries {
public static void main(String[] args) {
int sum = arthmeticSeries(2);
System.out.println(sum);
}
private static int arthmeticSeries(int x) {
//base case.
if (x == 0)
return 0;
Example: Arithmetic Series
2. //base case.
if (x == 0)
return 0;
//recursive case.
return x + arthmeticSeries(x - 1);
}
}
Example: cafeteria plates.
A stack is a FIFO data structure.
Each method call produces a stack frame (like a plate).
Information about the method.
Method arguments.
Information on how to return to the caller.
Each frame consists of:
What does recursion look like on the call stack?
The Fibonacci sequence a(1), a(2), a(3), ..., a(n), ... is defined by
a(1) =1
a(2) = 1
a(n) = a(n-1) + a(n-2)
, for all n > 2
This generates the sequence:
1, 1, 2, 3, 5, 8, 13, 21, ...
fibonacci(7) == 13
public class Fibonacci {
public static void main(String[] args) {
int num = fib(7);
System.out.println(num);
}
private static int fib(int x) {
if (x == 0)
return 0;
if (x == 1)
return 1;
Example: Fibonacci sequence
3. return 0;
if (x == 1)
return 1;
return fib(x - 1) + fib(x - 2);
}
}
public class BinarySearch {
public static void main(String[] args) {
int myArray[] = {41, 62, 77, 80, 85, 92, 104, 211, 400};
int pos = binsearch(myArray, 0, myArray.length, 62);
System.out.println(pos);
pos = binsearch(myArray, 0, myArray.length, 97);
System.out.println(pos);
}
private static int binsearch(int arr[], int start, int end, int val)
{
if ( start > end ) //if empty.
return -1; //not found.
int mid = (start + end) / 2;
if ( arr[ mid ] == val )
return mid; //found it!
else if ( arr[ mid ] < val )
return binsearch(arr, mid + 1, end, val);
else //arr[ mid ] > val
return binsearch(arr, start, mid - 1, val);
}
}
ex: 41 62 77 80 85 92 104 211 400
- search for 62
- check index 4 ((0+8)/2)
- (0+3)/2
- report found
- search for 97
- 97 > 85
- (5+8)/2 = 6
- 97 < 104
- (5+5)/2 = 5
- 97 > 92
Example: Binary Search
4. - (5+8)/2 = 6
- 97 < 104
- (5+5)/2 = 5
- 97 > 92
- use low and high index values to find that there are no more values to
search
Consider the sorting problem. That is, given a list of comparable
items (say an array of {bf int}s) in arbitrary order, rearrange that list
of items so that they are in {it non-decreasing} order. For example, say
you have an array of {bf int}s: $langle 38, 27, 43, 3, 9, 82, 10
rangle$. A sorted version of this array would be $langle 3, 9, 10, 27,
38, 43, 82 rangle$. Next, consider an efficient, divide and conquer
algorithm that can be used to {it recursively} solve the sorting problem
as follows:
begin{enumerate}
item Divide the unsorted list into two sublists of about half the
size.
item Divide each of the two sublists recursively until we have list
sizes of length $1$, in which case the list itself is returned
(after all, a list containing only $1$ element is always considered
sorted!).
item Merge the two sublists back into one sorted list.
end{enumerate}
Suppose now that you had the following function available to you:
begin{lstlisting}{}
void merge(Item arr[], const int& left, const int& right,
const int& pivot);
end{lstlisting}
that combines two {bf sorted} sub-arrays of {tt arr} (i.e., the inclusive
intervals [{tt left}, {tt pivot}] and [{tt pivot}$+1$, {tt right}]) to a
underline{single} {bf sorted} array. The result of this {it combined},
sorted array is stored back into {tt arr} to form a single, sorted array
whose valid elements are contained in the interval [{tt left}, {tt right}].
{tt Item} refers to a type (possibly through use of a {bf typedef}) whose
members are comparable (e.g., {bf int}).
/*
* 'a' is an array of 'Items' whose valid elements are between 'start' and 'end',
* inclusively. 'mid' is the position in 'a' in which to divide the array.
*/
void mergesort(Item a[], const int& start, const int& end, const int& mid) {
if ( start >= end ) //the array is either empty or contains a single element.
return; //sorting problem already solved. Nothing to do.
Example: Mergesort
5. if ( start >= end ) //the array is either empty or contains a single element.
return; //sorting problem already solved. Nothing to do.
//sort the left portion of the array.
mergesort(a, start, mid, (start + mid) / 2);
//sort the right portion of the array
mergesort(a, mid + 1, end, (mid + 1 + end) / 2);
//combined the two sorted array portions in a single sorted array.
merge(a, start, end, mid);
}