Big o 大O表示法-O(N)斐波那契发生器
只是在面试前复习一下我的大O 在《破解编码访谈》(第六版)第53页和第54页,在关于大O的章节中,您将看到示例15,如下所示Big o 大O表示法-O(N)斐波那契发生器,big-o,fibonacci,memoization,Big O,Fibonacci,Memoization,只是在面试前复习一下我的大O 在《破解编码访谈》(第六版)第53页和第54页,在关于大O的章节中,您将看到示例15,如下所示 void allFib(int n){ int[] memo = new int[n+1]; for(int i=0; i<n; i++){ System.out.println(i + ":" + fib(i,memo)); } } int fib(int n, int[] memo){ if(n<=0) r
void allFib(int n){
int[] memo = new int[n+1];
for(int i=0; i<n; i++){
System.out.println(i + ":" + fib(i,memo));
}
}
int fib(int n, int[] memo){
if(n<=0) return 0;
else if(n==1) return 1;
else if(memo[n]>0) return memo[n];
memo[n]=fib(n-1,memo)+fib(n-2,memo);
return memo[n];
}
最终编辑:
谢谢大家。我有我的答案。即使对fib方法的单个调用也受益于记忆,因此不是O(N^2),它也是O(N)。它是O(N)
,因为每个值只计算一次,以后再使用计算出的值。例如,对于fib(4)
这将是调用堆栈:
fib(4) = fib(3) + fib(2);
fib(3) = fib(2) + fib(1);
fib(2) = fib(1) + fib(0); // value stored
fib(1) = 1;
fib(0) = 1;
fib(2) = 2; // value accessed
存储值在已计算时被访问
当您看到斐波那契数的“惰性实现”时:
int fib(int i) {
if(i <= 1) return 1;
return fib(i - 1) + fib(i - 2);
}
如您所见,fib(2)
、fib(1)
和fib(0)
需要多次计算。使用内存方法,每个新值只计算一次。另外,请记住,调用堆栈会随着Fibonacci值的增加而呈指数级增大,这会导致更多的数字“双重计算”。这就是为什么这种惰性方法有O(2^N)
,而内存方法是O(N)
HTH一种看待这一点的方法是在
memo[n]=fib(n-1,memo)+fib(n-2,memo)
当fib(n-1,memo)
返回时,fib(n-2,memo)
所需的值已经存储在memo
中(if(memo[n]>0)return memo[n]
),这在每个递归级别都会重复
因此,与调用图看起来像这样不同(对于naive版本):
看起来像这样
*
* *
* *
* *
你必须小心这里的N
allFib(n)
在O(n)中,其中n是您传递的数字。它不是在O(N)中,其中N是输入的大小(以位为单位)。当然,最好的方法是使用迭代而不是递归。我仍然不确定我是否理解。如果我直接调用fib(n,memo),我必须计算出fib(1),fib(2),fib(3)。。。fib(n)。这将是O(N^2),就像非备忘录版本一样,因为备忘录是空的。添加一个循环来反复调用函数会增加更多的工作,而不是减少。你仍然需要计算fib(1),fib(2)。。。fib(n),但是您有固定的时间工作来添加和检索备忘录中的值。在这两种情况下,n的定义都是你解析它的数字,而不是输入的大小(以位为单位)。现在有意义了,请看最后的编辑和David Soroko的回答。坦率地说,当你提出这样的解决方案时,我不会给你这份工作。迭代法更简单、更直接。假设我们在n=50时调用上面的第二种方法。没有循环调用它50次,只有递归。这是O(N^2),因为备忘录没有用处。它在开始时为空,存储的值永远不会被重新访问。现在用50调用第一个方法。它从1循环到50,调用fib(n)50次。每次它叫它,它做的工作比以前的实验少,但它叫它50次。。。加上从备忘录中添加和检索的O(1)开销。更多的工作,而不是更少,但它是O(N)?当你从fib(49,memo)+fib(48,memo)开始时,首先要执行的是左手呼叫:fib(49,memo),然后fib(48,memo),fib(47,memo),等等。。。当这个调用链(深度为50)开始返回时,每一步都会执行右侧调用并立即返回,因为该值已被memorizedTanks David。这是有道理的。因此,即使我只调用一次具有大n值的fib方法,它仍然会从记忆中受益。
int fib(int i) {
if(i <= 1) return 1;
return fib(i - 1) + fib(i - 2);
}
fib(4) = fib(3) + fib(2);
fib(3) = fib(2) + fib(1);
fib(2) = fib(1) + fib(0);
fib(1) = 1;
fib(0) = 1;
fib(2) = fib(1) + fib(0);
fib(1) = 1;
fib(0) = 1;
*
* *
* * * *
* * * * * * * *
*
* *
* *
* *