堆栈中将存储多少调用?

How many calls will be stored in stack?

谁能解释一下下面的递归方法将在堆栈中存储多少次调用,为什么?什么时候存储在堆栈中?显然是 49 次调用,但我不明白为什么。谢谢。

public static long fib( int n ){ // n = 50
    if( n <= 1 )
        return 1;
    else
        return fib( n - 1 ) + fib( n - 2 );
    }

堆栈中对 fib 的调用次数是 fib 需要被调用计算的次数 fib(n)

fib(50) 将调用 fib 函数超过 49 次。

举个小例子。如果您调用 fib(3),这将调用 fib(2)fib(1)fib(2) 将调用 fib(0)fib(1)。这已经是 fib(3) 的 5 次 fib 调用。

对于较大的 n 值,每次调用 fib(3) 将导致总共调用 5 次,因为这些值未存储在任何地方。 fib 求值的唯一方法是为两个较小的值调用自身。

斐波那契数列是错误使用递归的标准示例,因为成本是指数级的。我会让你算出到底需要多少电话。

下面的递归方法将在堆栈中存储多少次调用,为什么?

输入为 50,在任何给定时刻,包括对 fib 的初始调用在内,堆栈帧的最大数量将为 50。(因此最多将有 49 个递归调用存储在在任何给定时刻堆叠)。

最多可以有 50 个堆栈帧的原因是:

fib(50)的初始调用是1个堆栈帧。

然后用这个语句,return fib( n - 1 ) + fib( n - 2 ); 对左侧 fib 的递归调用将首先执行,将 fib(49) 放入堆栈。我们将再次点击 return 行,将 fib(48) 放入堆栈。这将重复,直到 fib(1) 在堆栈中命中 return 1; 语句。这会将 return 变为 fib(2) 并允许在语句 return fib( n - 1 ) + fib( n - 2 ); 中执行正确的递归调用。

长话短说,所有左递归调用甚至在 1 个右递归调用之前执行。所以很容易看出最后一次递归调用会有 fib(50)fib(49)、...,最后是 fib(1),导致执行 [= 时最多有 50 个堆栈帧23=].

什么时候存储在堆栈中?

当你调用一个函数时,一个栈帧被分配,在函数returns之后,栈帧被释放。甚至 main 的函数调用也存储在堆栈中。然而,堆栈框架可能会被优化掉,但这是一个更高级的话题。

如果您想了解更多有关堆栈帧优化的信息,请参阅我小时候问过的这个问题:

Does a stack frame really get pushed onto the stack when a function is called?

补充编辑: 如果您想通过视觉了解递归是如何发生的,请将您的手指放在根的顶部,然后首先沿着左侧沿着树的外部追踪:

将堆栈框架想象成您发送给您以获取一些信息的人类工作者。暂时忘掉斐波那契数列,想想一个更简单的递归公式,求和。

int sum(int n) {
  if (n == 1) {
    return 1;
  } else {
    return sum(n - 1) + n;
  }
}

sum(1) = 1

sum(2) = sum(1) + 2 = 1 + 2 = 3

等...

现在想象一下,当您调用 sum(n) 时,您会派人去为您查找结果。在他给你你要求的结果之前,他不会 "finished" 工作。但是,他还需要调用 sum(n - 1),这只能通过让另一个人工作来完成。现在我们有两个人同时工作!您要问的问题是 同时工作的最大人数是多少? 如果您包括您自己,对于上面实施的求和算法和实施斐波那契数列。