斐波那契数列递归与非递归算法时间复杂度分析
斐波那契数列定义:
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=0或n=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)=0、F(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