1. Recursion lecture in C++
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-1) + (x-2) + .. + 0, for x >= 0
* Example where x = 3, we have 3 + 2 + 1 + 0 = 6
*/
#include <iostream>
using namespace std;
//function declaration.
//finds the solution to the arthmetic series starting a x for x >= 0.
int arthmeticSeries(int x);
int main() {
int sum = arthmeticSeries(3);
cout << “The solution to x = 3 is “ << sum << endl; //prints 6.
}
Example: Arithmetic Series
2. int main() {
int sum = arthmeticSeries(3);
cout << “The solution to x = 3 is “ << sum << endl; //prints 6.
}
//function definition.
arthmeticSeries(int x) {
//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?
#include <iostream>
using namespace std;
int binsearch(int arr[], int start, int end, int val);
int main() {
int myArray[] = {41, 62, 77, 80, 85, 92, 104, 211, 400};
int length = 9;
int pos = binsearch(myArray, 0, length - 1, 62);
cout << pos << endl;
pos = binsearch(myArray, 0, length - 1, 97);
cout << pos << endl;
}
int binsearch(int arr[], int start, int end, int val) {
if ( start > end ) //if empty.
return -1; //not found.
Example: Binary Search
3. 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
- 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}
Example: Mergesort
4. 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:
void merge(Item arr[], const int& left, const int& right,
const int& pivot);
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.
//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);
}