C语言递归函数f(4)栈帧结构、Big Theta复杂度及调用次数问询
Hey there! Let's walk through each of your questions about this recursive function clearly—recursion stack behavior and complexity are classic topics that are worth unpacking in detail.
First, let's restate the function for reference:
unsigned f(unsigned n) { if(n<=1) return 1; return f(n-2)+f(n-2); }
1. Stack Frame Structure When Calling f(4)
Let's trace the call sequence and corresponding stack frames step by step. Remember, each stack frame typically holds:
- The return address (where the program jumps back to after the function returns)
- Function parameters (here, just
n) - Space for the return value
- Any local variables (this function has none, so we can ignore that)
Here's how the stack evolves:
- Initial call: Your main function calls
f(4). A stack frame forf(4)is pushed onto the stack, withn=4and return address pointing to the main function's call site. This is the current top of the stack. - First
f(2)call:f(4)executesreturn f(2)+f(2), so it first calls the firstf(2). A new stack frame forf(2)(n=2, return address pointing back tof(4)'s firstf(2)call) is pushed on top. - First
f(0)call:f(2)executes its return statement, calling the firstf(0). Stack frame forf(0)(n=0, return address pointing tof(2)'s firstf(0)call) is pushed. f(0)returns: Since0<=1,f(0)returns 1. Its stack frame is popped off the stack, and we're back tof(2)with the first return value (1) stored.- Second
f(0)call:f(2)now calls the secondf(0). Another stack frame forf(0)(n=0, return address pointing tof(2)'s secondf(0)call) is pushed. f(0)returns again: Thisf(0)also returns 1. Its stack frame is popped,f(2)calculates1+1=2, returns that value, and its stack frame is popped. We're back tof(4)with the firstf(2)'s return value (2).- Second
f(2)call:f(4)now calls the secondf(2). The entire sequence above repeats: pushf(2)frame, push twof(0)frames, each returns 1,f(2)returns 2, stack frame popped. f(4)returns: Finally,f(4)calculates2+2=4, returns that value, and its stack frame is popped, returning to the main function.
At the peak of stack usage (when we're in the first f(0) call from the first f(2)), the stack has 3 frames: f(4) → f(2) → f(0).
2. Big Theta Time Complexity
Let's derive the recurrence relation first:
- For
n <= 1,f(n)runs in constant time: Θ(1) - For
n > 1,f(n) = 2 * f(n-2)(since we make two calls tof(n-2)and do a constant-time addition)
Let's expand this for even n first (since f(4) is even):
f(4) = 2*f(2) = 2*(2*f(0)) = 2^2 * 1 = 4- In general, for
n=2k,f(n) = 2^k * f(0) = 2^(n/2)
For odd n (e.g., f(3)), it's 2^((n-1)/2) * f(1) = 2^((n-1)/2)
In both cases, the number of function calls grows exponentially with n, specifically as Θ(2^(n/2))—which is equivalent to Θ((√2)^n). To put it in terms of recursion trees: each node has two children, and the depth of the tree is roughly n/2 (since we subtract 2 each time). The total number of nodes (function calls) is proportional to 2^(n/2), hence the time complexity.
3. Are f(n-2)+f(n-2) One or Two Calls?
This counts as two separate function calls.
Semantically, in C, each occurrence of f(n-2) is a distinct function call expression. Even though the argument is the same, the compiler will generate code to invoke the function twice—each call will push its own stack frame, execute the function body from start to finish, and return a value.
While some optimizing compilers might detect this redundancy and optimize it into a single call plus a multiplication (like 2 * f(n-2)), that's an optimization, not the default semantic behavior. From the language's perspective and without optimizations, it's definitely two independent calls.
内容的提问来源于stack exchange,提问作者Perke

