剑指 Offer 10- I. 斐波那契数列

Posted 炫云云

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指 Offer 10- I. 斐波那契数列相关的知识,希望对你有一定的参考价值。

剑指 Offer 10- I. 斐波那契数列

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F ( 0 ) = 0 ,     F ( 1 )   = 1 F ( N ) = F ( N − 1 ) + F ( N − 2 ) , 其 中 N > 1. F(0) = 0,   F(1) = 1\\\\ F(N) = F(N - 1) + F(N - 2), 其中 N > 1. F(0)=0,  F(1) =1F(N)=F(N1)+F(N2),N>1.

斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

动态规划

创建长度为n+1的数组dp,令dp[0]=0,dp[1]=1。除前两项外,数组中某项的值为其前两项的和,据此由前到后依次求出各项的值,最后一项即为所求

class Solution(object):
    def fib(self, n):
        """
        :type n: int
        :rtype: int
        """
        if n <= 0:
            return 0
        F0 = 0
        F1 =1
        for _ in range(1,n):
            F1 , F0 = F0+F1, F1
        return  F1%1000000007

递归

通常情况下,递归是一种直观而有效的实现算法的方法。 但是,如果我们不明智地使用它,可能会给性能带来一些不希望的损失,例如重复计算。提出一种常用的技术,称为记忆化(memoization),可以用来避免这个问题。

基本函数:

def fib(self, n):
    """
    :type n: int
    :rtype: int
    """
    if n <2:
        return n
    else:
        return (fib(n-1) +fib(n-2)) %1000000007

例如F(4)的计算:
F ( 4 ) = F ( 3 ) + F ( 2 ) = ( F ( 2 ) + F ( 1 ) ) + F ( 2 ) F(4) = F(3) + F(2) = (F(2) + F(1)) + F(2) F(4)=F(3)+F(2)=(F(2)+F(1))+F(2)
为了得到 f (4)的结果,需要在上述推导之后计算两次数 F(2) : 第一次在 F(4) 的第一次展开中,第二次在中间结果 F(3) 中。

下面的树显示了在计算 F(4) 时发生的所有重复计算(按颜色分组)。

  • 复杂度

上面是递归的执行树,这是一个用于表示递归函数的执行流程的树。树中的每个节点都表示递归函数的调用。因此,树中的节点总数对应于执行期间的递归调用的数量。

在 n 层的完全二叉树中,节点的总数为 2 n − 1 {2^n -1} 2n1。因此 f(n) 中递归数目的上限(尽管不严格)也是 2 n − 1 {2^n -1} 2n1。那么我们可以估计 f ( n ) f(n) f(n) 的时间复杂度为 O ( 2 n ) {\\mathcal{O}(2^n)} O(2n)

记忆化

为了消除上述情况中的重复计算,正如许多人已经指出的那样,其中一个想法是将中间结果存储在缓存中,以便我们以后可以重用它们,而不需要重新计算。

这个想法也被称为记忆化,这是一种经常与递归一起使用的技术。

对于 F ( n ) F(n) F(n)​,可以使用哈希表来建立 n : F ( n ) {n:F(n)} n:F(n),保存之前的结果,可以避免重复计算。 记忆化技术是一个很好的例子,它演示了如何通过增加额外的空间以减少计算时间。

代码:

def fib(self, n):
    """
    :type n: int
    :rtype: int
    """
    hanshi = {}
    def rnn_fib(N):
        if n in hanshi[n]:
            return hanshi[n]
        if n<2:
            result = n
        else:
            result = rnn_fib(n-1) +rnn_fib(n-2)
        hanshi[n] = result
        return result
    return  rnn_fib(n)

通过记忆化技术,我们保存每个索引 n 对应的的斐波那契数的结果。我们确信每个斐波那契数的计算只会发生一次。而从递推关系来看,斐波纳契数 f(n) 将取决于其所有 n-1 个先验斐波纳契数。结果,计算 f(n) 的递归将被调用 n-1 次以计算它所依赖的所有先验数字。

时间复杂度,即 O ( 1 ) ∗ n = O ( n ) {\\mathcal{O}(1) * n = \\mathcal{O}(n)} O(1)n=O(n)

非递归相关空间

正如名称所示,非递归相关空间指的是与递归过程没有直接关系的内存空间,通常包括为全局变量分配的空间(通常在堆中)。

不管是否递归,你都可能需要在任何函数调用之前将问题的输入存储为全局变量。你可能还需要保存递归调用的中间结果。后者就是记忆化技术。例如,使用映射(map)来跟踪在递归调用期间产生的所有中间斐波那契数。因此,在分析空间复杂度时,应该考虑到因采用记忆化技术所导致的空间成本

参考

还需要保存递归调用的中间结果。后者就是记忆化技术。例如,使用映射(map)来跟踪在递归调用期间产生的所有中间斐波那契数。因此,在分析空间复杂度时,应该考虑到因采用记忆化技术所导致的空间成本

参考

Krahets - 力扣(LeetCode) (leetcode-cn.com)

以上是关于剑指 Offer 10- I. 斐波那契数列的主要内容,如果未能解决你的问题,请参考以下文章

剑指 Offer 10- I. 斐波那契数列

《剑指Offer——10- I. 斐波那契数列,10- II. 青蛙跳台阶问题63. 股票的最大利润》代码

剑指 Offer 10- I. 斐波那契数列

[LeetCode]剑指 Offer 10- I. 斐波那契数列

[LeetCode]剑指 Offer 10- I. 斐波那契数列

leetcode剑指 Offer 10- I. 斐波那契数列