Now that we have analyzed factorial, we can apply the same logic to any other recursive function that does a constant amount of work per call and makes a single recursive call to repeat the process with one less than the current input. Any function that looks like:
There are other common recurrence relations that we will see in recursive functions. Another common one is a function that makes a recursive call on half of the current input. For example, consider a function that looks like this:
A final common recurrence relation is one where a function a linear (\(O(n)\)) amount of work and then makes two recursive calls on half of the current input. Here is an example of such a function:
int mystery(int n) {
if (n <= 1) {
// O(1) work
} else {
// O(n) work
for (int i = 0; i < n; i++)...
// Two recursive calls
mystery(n / 2);
mystery(n / 2);
// O(1) work
}
}
Here, the time to do a job of size \(n\) is the time to do two jobs of size \(n / 2\) plus \(O(n)\) work for the loop. This gives us the recurrence relation:
int mysteryFunction(int start, int end) {
if (end > start + 1) {
int mid = (start + end) / 2;
mysteryFunction(start, mid);
mysteryFunction(mid, end);
for (int i = start; i < end; i++) {
// do O(1) work
}
}
}
\(T(n) = T(n / 2) + O(1) \)
\(T(n) = T(n - 1) + O(1) \)
\(T(n) = 2 \cdot T(n / 2) + O(n) \)
The size here is \(n = end - start\text{.}\)
The function makes two recursive calls on half the input size and does linear work outside the recursive calls.