数据结构-算法

斐波那契数列递归与非递归算法时间复杂度分析

斐波那契数列定义:
F(n)={0,n=01,n=1F(n−1)+F(n−2),n>1 F(n)= \begin{cases} 0, & n=0 \\ 1, & n=1 \\ F(n-1)+F(n-2), & n>1 \end{cases} F(n)=⎩ ⎨ ⎧0,1,F(n−1)+F(n−2),n=0n=1n>1


一、递归算法的时间复杂度分析

1. 算法逻辑

直接按照数学定义实现,每次计算F(n)时,递归调用F(n-1)F(n-2),直到递归到基线条件n=0n=1

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

// 递归实现斐波那契数列
long long fib_recursive(int n) {
    // 基线条件:n=0 返回0,n=1 返回1
    if (n == 0) return 0;
    if (n == 1) return 1;
    // 递归调用:F(n) = F(n-1) + F(n-2)
    return fib_recursive(n - 1) + fib_recursive(n - 2);
}

int main() {
    int n;
    printf("请输入要计算的斐波那契数列项数 n:");
    scanf("%d", &n);

    if (n < 0) {
        printf("请输入非负整数!\n");
        return 1;
    }

    printf("递归版:F(%d) = %lld\n", n, fib_recursive(n));
    return 0;
}

2. 时间复杂度递推关系

T(n)为计算F(n)的时间开销:

  • 基线条件:n=0/n=1时,直接返回结果,时间为常数,即T(0)=T(1)=O(1)
  • 递归条件:n>1时,需执行2次递归调用(T(n-1)+T(n-2))+1次加法(常数时间O(1)),递推式为:
    T(n)=T(n−1)+T(n−2)+O(1) T(n) = T(n-1) + T(n-2) + O(1) T(n)=T(n−1)+T(n−2)+O(1)

3. 递推式求解

该递推式的特征方程为r² - r - 1 = 0,解得特征根:
r1=1+52≈1.618 (黄金分割比 ϕ),r2=1−52≈−0.618 r_1 = \frac{1+\sqrt{5}}{2} \approx 1.618 \ (\text{黄金分割比}\ \phi),\quad r_2 = \frac{1-\sqrt{5}}{2} \approx -0.618 r1=21+5 ≈1.618 (黄金分割比 ϕ),r2=21−5 ≈−0.618

通解为T(n) = A·r₁ⁿ + B·r₂ⁿ,代入初始条件后可得:T(n)与斐波那契数列F(n)同阶,因此时间复杂度为:
T(n)=O(ϕn)=O(2n) (指数级时间复杂度) \boldsymbol{T(n) = O(\phi^n) = O(2^n)} \ (\text{指数级时间复杂度}) T(n)=O(ϕn)=O(2n) (指数级时间复杂度)

4. 空间复杂度

递归调用会产生调用栈,最大深度为n(从F(n)F(1)的递归链),因此空间复杂度为O(n)


二、非递归(迭代)算法的时间复杂度分析

1. 算法逻辑

从基线条件F(0)=0F(1)=1出发,通过循环依次计算F(2)F(n),每次仅保存前两项的值,无重复计算。

c 复制代码
#include <stdio.h>

// 迭代实现斐波那契数列
long long fib_iterative(int n) {
    // 边界处理
    if (n == 0) return 0;
    if (n == 1) return 1;

    long long a = 0;  // F(0)
    long long b = 1;  // F(1)
    long long c;      // 存储当前计算结果

    // 循环从 2 计算到 n
    for (int i = 2; i <= n; i++) {
        c = a + b;    // F(i) = F(i-1) + F(i-2)
        a = b;        // 更新前一项
        b = c;        // 更新当前项
    }
    return c;
}

int main() {
    int n;
    printf("请输入要计算的斐波那契数列项数 n:");
    scanf("%d", &n);

    if (n < 0) {
        printf("请输入非负整数!\n");
        return 1;
    }

    printf("迭代版:F(%d) = %lld\n", n, fib_iterative(n));
    return 0;
}

2. 时间复杂度分析

循环从i=2执行到i=n,共执行n-1次,每次循环仅包含常数时间的加法、赋值操作,因此时间复杂度为:
T(n)=O(n) (线性时间复杂度) \boldsymbol{T(n) = O(n)} \ (\text{线性时间复杂度}) T(n)=O(n) (线性时间复杂度)

3. 空间复杂度

仅使用3个变量存储中间结果(F(i-2)F(i-1)F(i)),与n无关,因此空间复杂度为O(1)(常数空间)。


三、两种算法的核心对比

算法类型 时间复杂度 空间复杂度 核心特点
递归算法 O(ϕn)O(\phi^n)O(ϕn)(指数级,近似O(2n)O(2^n)O(2n)) O(n)O(n)O(n)(递归栈) 实现直观,但存在大量重复计算(如F(n-2)F(n)F(n-1)重复计算),仅适合理论分析,工程中不可用
非递归(迭代)算法 O(n)O(n)O(n)(线性级) O(1)O(1)O(1)(常数空间) 无重复计算,时间/空间效率极高,是工程实践的首选方案

补充:递归算法的优化(记忆化搜索)

若对递归算法添加「备忘录」(存储已计算的F(k),避免重复计算),可将时间复杂度优化为O(n),但空间复杂度仍为O(n)(存储备忘录),整体效率仍低于迭代算法。


关键结论

  • 朴素递归算法是指数时间 ,仅适合教学演示,无法处理较大的n
  • 迭代算法是线性时间+常数空间,是斐波那契数列计算的最优工程实现。

线性表的基本操作分析

c 复制代码
#include <stdio.h>

// test函数:参数为int类型,采用值传递
void test(int x) {
    x = 1024;          // 修改的是形参x(main中x的副本)
    printf("test函数内部 x=%d\n", x);
}

int main() {
    int x = 1;
    printf("调用test前 x=%d\n", x);
    test(x);           // 传递x的值1,形参是独立副本
    printf("调用test后 x=%d\n", x); // main中x不会被修改
    return 0;
}
c 复制代码
调用test前 x=1
test函数内部 x=1024
调用test后 x=1

C 语言默认是值传递:

调用test(x)时,系统为形参x分配独立内存,把main中x的值1复制给形参。

test内修改的是形参副本,和main中的x是两个完全独立的变量,因此main中的x不会改变。

c 复制代码
#include <stdio.h>

// test函数:参数为int*类型,传递x的内存地址
void test(int *x) {
    *x = 1024;         // 通过地址直接修改main中x的值
    printf("test函数内部 x=%d\n", *x);
}

int main() {
    int x = 1;
    printf("调用test前 x=%d\n", x);
    test(&x);          // 传递x的地址&x
    printf("调用test后 x=%d\n", x); // main中x被修改为1024
    return 0;
}
c 复制代码
调用test前 x=1
test函数内部 x=1024
调用test后 x=1024
相关推荐
HalvmånEver2 小时前
Linux:基于TCP Socket的在线翻译
linux·运维·服务器·网络·学习·tcp/ip
BIBI20492 小时前
VirtualBox 7.x 安装 Ubuntu 24 及增强功能配置、克隆虚拟机教程
linux·windows·ubuntu·环境搭建·安装教程·最佳实践·virtualbox
weixin_462901972 小时前
HICKPI主板h618 Ubuntu / Armbian 镜像 SD安装
linux·运维·ubuntu
A.A呐2 小时前
【Linux第二十四章】IP协议
linux·网络
llkk星期五2 小时前
ubuntu(24.04)下wayland显示界面带来的影响
linux·ubuntu
hsjcjh11 小时前
Nodemailer使用教程:在Node.js中发送电子邮件
linux·运维·node.js
不怕犯错,就怕不做12 小时前
linux 如何查看自己的帐号密码及samba的帐号和密码
linux·运维·服务器
地下核武12 小时前
Ubuntu 24.04 在线安装 Qt 6.10.2 后 Qt Creator 无法启动问题记录与解决
linux·qt·ubuntu
张32312 小时前
Linux 启动过程
linux·运维