斐波那契数列的三种解法及时间复杂度

本文介绍了解决斐波那契数列问题的三种方法:递归、尾递归及循环。详细分析了每种方法的时间复杂度和空间复杂度,并通过实例对比了它们之间的优劣。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

斐波那契数列:
f(n)=f(n-1)+f(n-2)(n>2) f(0)=1;f(1)=1;
即有名的兔子繁衍问题
在本篇文章我将会给出三种解法
递归

(1)递归:函数自己调用自己
(2)递归的"缺陷":递归到一定程度,会发生"栈溢出"
(3)递归的"时间复杂度":递归总次数*每次递归的次数
(4)递归的"空间复杂度":递归的深度*每次递归空间的大小(注意:"每次递归空间的大小"是个常数,可以基本忽略不计)
    递归的"深度":树的高度(递归的过程是一个"二叉树"

1.递归实现斐波那契数列

#include<stdio.h>
#include<stdlib.h>
long long Fib(long long N)
{
    if (N < 3)
        return 1;
    else
        return Fib(N - 1) + Fib(N - 2);
}
int main()
{
    long long num = 0;
    num=Fib(10);
    printf("递归:%d\n", num);
    system("pause");
    return 0;
}

运行结果:
这里写图片描述

此种方法的缺陷:重复计算的次数太多,效率低
   例如:在下图中,F(3)就重复计算了 "3次"
时间复杂度:O(2^N)
空间复杂度:O(N)

这里写图片描述

2.递归(尾递归)实现斐波那契数列,但是时间复杂度尽可能低

尾递归是什么呢?
尾递归解决了递归重复计算的问题

"尾递归的前提是递归"
(1)定义:在一个程序中,执行的最后一条语句是对自己的调用,而且没有别的运算

(2)尾递归的实现:是在编译器优化的条件下实现的

  编译器优化:
     递归的第一次调用时会开辟一份空间,此后的递归调用不会再开辟空间,而是在刚才开辟的空间上做一些修改,实现此次递归,例如在本题中求Fib(10),编译器会给Fib(10)的调用开辟栈帧,调用Fib(9)的时候不会再重新开辟栈帧,而是在刚开辟的栈帧上做一些修改,因为递归的每一次调用都是一样的流程,只是会有一些数据不同,所以不会再开辟空间。

注:vs一般都支持优化,Debug下编译器不会优化哦,一定要在Release模式下。
#include<stdio.h>
#include<stdlib.h>
long long Fib(long long first,long long second ,long long N)
{
    if (N < 3)
        return 1;
    if (N == 3)
        return first + second;
    return Fib(second, first + second, N - 1);
}
int main()
{
    long long num = 0;
    num=Fib(1,1,10);
    printf("尾递归:%d\n", num);
    system("pause");
    return 0;
}

运行结果:

这里写图片描述

此种方法是尾递归,很大程度的减小了第一种方法(递归实现斐波那契数列)的时间复杂度
时间复杂度:O(N-2)约等于0(N)
空间复杂度:O(N-2)约等于0(N)(编译器如果优化的话是O1))

此种递归是尾递归

3.循环实现斐波那契数列

#include<stdio.h>
#include<stdlib.h>
long long Fib(long long N)
{
    long long first = 1;
    long long second = 1;
    long long ret = 0;
    for (int i = 3; i <=N; ++i)
    {
        ret = first + second;
        first = second;
        second = ret;
    }
    return second;
}
int main()
{
    long long num = 0;
    num=Fib(10);
    printf("循环:%d\n", num);
    system("pause");
    return 0;
}

运行结果:

这里写图片描述

时间复杂度:O(N)
空间复杂度:O(1)(创建了四个对象,是常数,所以可忽略不计)
此种方法是"最优方法"
优点:时间复杂度和空间复杂度最低,而且可读性高
### 斐波那契数列递归实现及其优化 斐波那契数列是一个经典的算法问题,在许多编程练习平台上都有涉及。对于给定的 \( n \),计算斐波那契数列第 \( n \) 项的任务可以通过多种方式完成,其中一种常见的方式就是使用递归来解决这个问题。 #### 基本递归实现 基本的递归实现遵循斐波那契数列的定义: \[ F(n) = \begin{cases} 0 & \text{if } n = 0 \\ 1 & \text{if } n = 1 \\ F(n-1) + F(n-2) & \text{otherwise} \end{cases} \] 下面是一段简单的 C 语言代码来展示如何通过递归计算斐波那契数列中的某一项[^4]: ```c #include <stdio.h> int fibonacci(int n){ if (n == 0) return 0; else if (n == 1 || n == 2) return 1; else return fibonacci(n - 1) + fibonacci(n - 2); } int main(){ int n; scanf("%d", &n); printf("%d\n", fibonacci(n)); return 0; } ``` 这段程序能够正确地处理输入并给出相应的输出结果。然而,当面对较大的数值时,这种纯递归的方法效率很低,因为它会重复计算相同的子问题多次。 #### 递归加记忆化技术 为了提高性能,可以在递归的基础上加入记忆化的技巧——即存储已经计算过的结果以便后续调用时可以直接读取而不是重新计算。这可以大大减少不必要的运算次数。 下面是改进后的版本,它利用数组 `memo` 来保存之前得到过的值[^3]: ```c #include <stdio.h> #define MAX_N 40 long long memo[MAX_N]; // 初始化备忘录 void init_memo() { for (int i = 0; i < MAX_N; ++i) { memo[i] = -1; } } long long fibo_with_memoization(int n){ if (n <= 1) { return n; } // 如果已经有记录,则直接返回 if(memo[n]!=-1){ return memo[n]; } // 否则先算出来再存进去 memo[n]=fibo_with_memoization(n-1)+fibo_with_memoization(n-2); return memo[n]; } int main(){ init_memo(); int n; scanf("%d",&n); printf("%lld\n",fibo_with_memoization(n)); return 0; } ``` 此方法显著提高了大数情况下求解的速度,并且保持了原生递归逻辑上的清晰度。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值