Given the function below:
int f(int n) {
if (n <= 1) {
return 1;
}
return f(n - 1) + f(n - 1);
}
I know that the Big O time
This can be better explained considering a different function
f(n) = f(n-1) + f(n-2)
f(0) =0, f(1)=1
which would result in following tree of calculation for f(4)
f(4)
f(3) f(2)
f(2) f(1) f(1) f(0)
f(1) f(0)
System can process the calculation with reduplicated storage stack equal to depth (storage unit for f(0), f(1), f(2), f(3) and f(4)). While runtime needs to account for all operations at each node (addition or return statement) - hence is a factor of none of the nodes.
Here's how I think about it:
A useful way to approach these types of problems is by thinking of the recursion tree. The two features of a recursive function to identify are:
Our recurrence relation for this case is T(n) = 2T(n-1)
. As you correctly noted the time complexity is O(2^n)
but let's look at it in relation to our recurrence tree.
C
/ \
/ \
T(n-1) T(n-1)
C
____/ \____
/ \
C C
/ \ / \
/ \ / \
T(n-2) T(n-2) T(n-2) T(n-2)
This pattern will continue until our base case which will look like the following image:
With each successive tree level, our n reduces by 1. Thus our tree will have a depth of n before it reaches the base case. Since each node has 2 branches and we have n total levels, our total number of nodes is 2^n
making our time complexity O(2^n)
.
Our memory complexity is determined by the number of return statements because each function call will be stored on the program stack. To generalize, a recursive function's memory complexity is O(recursion depth)
. As our tree depth suggests, we will have n total return statements and thus the memory complexity is O(n)
.