数据结构-算法

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

斐波那契数列定义:
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
相关推荐
cui_ruicheng14 小时前
Linux网络编程(二):网络数据传输基本流程
linux·服务器·网络
怀旧,14 小时前
【Linux网络编程】15. Reactor 反应堆模式
linux·网络·php
小赵不会秃头15 小时前
数据结构Day 06:线性结构、库操作及 Makefile 完整学习笔记
java·linux·数据结构·算法·面试
雨田大大15 小时前
Windows11下IDEA运行后端时,端口被占用的解决方法
linux·运维·服务器
IKun-bug15 小时前
CentOS 7 安装 Claude Code 指南
linux·运维·centos
kdxiaojie15 小时前
U-Boot分析【学习笔记】(8)
linux·笔记·学习
风曦Kisaki15 小时前
# Linux运维Day02:LNMP架构部署、动静分离原理、Nginx地址重写、systemd服务管理
linux·运维·架构
Shadow(⊙o⊙)15 小时前
Linux进程地址空间——钻入Linux内核架构性剖析 硬核手搓!
java·linux·运维·服务器·开发语言·c++
大明者省15 小时前
乌邦托服务器系统www不同文件夹bird、infra建立隔离的虚拟环境
linux·运维·服务器
kobe_OKOK_15 小时前
ubuntu server设置 NTP 服务器
linux·服务器·ubuntu