<LeetCode> 题366:斐波那契数列

1. 题目描述:

查找斐波纳契数列中第 N 个数。
所谓的斐波纳契数列是指:前2个数是 0 和 1 。第 i 个数是第 i-1 个数和第i-2 个数的和。斐波纳契数列的前10个数字是:0, 1, 1, 2, 3, 5, 8, 13, 21, 34 …
例如:
给定 1,返回 0
给定 2,返回 1
给定 10,返回 34

2. 思路1:递归求解

long long fib(unsigned int n)  //递归求解
    {
        if(n <= 2)
            return n - 1;
        else
            return fib1(n - 2) + fib1(n - 1);
    }

注意:递归操作,在LeetCode提交时,不通过,显示超时。
原因如下:
递归来解决这一问题,会带来严重的效率问题。比如:要求f(10),如下图所示:
这里写图片描述
可以看到,上面图示中有很多重复结点,而且重复结点会随着n的增大而急剧增加,意味着计算量也会随着n的增大而急剧增加。事实上该算法的时间复杂度随着n值呈指数增长O((3/2)^n)

3. 思路2:迭代求解

这种迭代思想就是:先根据f(1)和f(2)计算出f(3),再根据f(2)和f(3)计算出f(4)……依次类推,最后计算出f(n)。可以看到迭代方法的时间复杂度只有O(n)

3.1 写法1:

    long long fib(unsigned int n)  //迭代求解
    {
        if(n <= 2)
            return n - 1;

        long long  first = 0;
        long long second = 1;

        for(unsigned int i = 3; i <= n ; i++)
        {
            second = first + second;
            first = second - first;
        }
        return second;
    }   

3.2 写法2:

    long long fib(unsigned int n)  //迭代求解
    {
        if(n <= 2)
            return n - 1;

        long long first = 0;
        long long second = 1;
        long long result;
        for(unsigned int i = 3; i <= n; i++)
        {
            result = first + second;
            first = second;
            second = result;
        }
        return result;
    }

4. 思路3:数组实现

    long long fib(unsigned int n) //数组实现求解
    {
        if(n <= 2)
            return n - 1;
        int *arr = new int[n + 1];
        arr[1] = 0;
        arr[2] = 1;
        for(unsigned int i = 3; i <= n; i++)
        {
            arr[i] = arr[i - 1] + arr[i - 2];
        }
        unsigned int result = arr[n];
        delete arr;
        return result;
    }

5. 思路4:向量实现

    long long fib(unsigned int n) //向量实现求解
    {
        if(n <= 2)
            return n - 1;
        vector<int> vec;
        vec.push_back(0);
        vec.push_back(1);
        vec.reserve(3); //预留空间
        for(unsigned int i = 2; i <= n; i++)
        {
            vec.insert(vec.begin(), vec[0] + vec[1]);
            vec.pop_back(); 
        }
        return vec[0];  
    }

6. 思路5:队列实现

    long long fib(unsigned int n) //队列实现求解
    {
        if(n <= 2)
            return n - 1;
        queue<int> q;
        q.push(0);
        q.push(1);
        for(unsigned int i = 2; i < n; i++)
        {
            q.push(q.front() + q.back());
            q.pop();
        }
        return q.back();    
    }

7. 思路6:通项公式求解

如果我们知道一个数列的通项公式,那么使用公式来计算就容易得多了。对于斐波那契数列来说,由递推公式f(n) = f(n - 1) + f(n - 2),可以得到f(n)的特征方程为:
x2 = x + 1;从而求出方程的根为:x=(1±√5)/2
所以,存在A,B使得:f(n) = A x ((1+√5)/2)n + B x ((1-√5)/2)n,代入f(0) = 0; f(1) = 1,解得 A=√5/5 ,B=-√5/5 , 即f(n) = √5/5 x ((1+√5)/2)n - √5/5 x ((1-√5)/2)n
通过公式,我们可以在O(1)的时间内得到f(n)。但公式中引入了无理数,所以不能保证结果的精度。

long long fib(unsigned int n) //通项公式求解
{
    double s = sqrt(5);
    return floor( (pow((1 + s)/2, n) - pow((1 - s)/2, n))/s + 0.5 );
}

注意:这种方法计算出来的结果是以f(0)开始的,也就是说,f(10) = 55(不是34)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值