递归代码:
// 递归实现斐波那契数列
int fun1(int n) {
if (n == 0 or n == 1) {
return 1;
}
return fun1(n - 1) + fun1(n - 2);
}
递归结果(部分):
迭代代码:
// 迭代实现斐波那契数列
int fun2(int f[], int n) {
int i;
f[0] = 1;
f[1] = 1;
for (i = 2; i <= n; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f[n];
}
迭代结果(部分):
源码:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// 递归实现斐波那契数列
int fun1(int n) {
if (n == 0 or n == 1) {
return 1;
}
return fun1(n - 1) + fun1(n - 2);
}
// 迭代实现斐波那契数列
int fun2(int f[], int n) {
int i;
f[0] = 1;
f[1] = 1;
for (i = 2; i <= n; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f[n];
}
int main() {
int n = 50;
int* f = (int*)malloc((n + 1) * sizeof(int));
clock_t start, end;
double time;
// 测试递归实现斐波那契数列的运行时间
printf("递归 实现斐波那契数列的运行时间:\n");
for (int i = 0; i <= n; i++) {
start = clock();
int result = fun1(i);
end = clock();
time = ((double)(end - start)) / CLOCKS_PER_SEC;//clock()函数返回的时钟计时单元数除以CLOCKS_PER_SEC可以得到以秒为单位的时间
printf("第%d个数: %d (消耗时间: %f秒)\n", i, result, time);
}
printf("\n");
// 测试迭代实现斐波那契数列的运行时间
printf("迭代 实现斐波那契数列的运行时间:\n");
for (int i = 0; i <= n; i++) {
start = clock();
int result = fun2(f, i);
end = clock();
time = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("第%d个数: %d (消耗时间: %f秒)\n", i, result, time);
}
free(f); // 释放动态分配的内存
return 0;
}
递归和迭代时间效率对比:
实验结果:由运行结果可视化分析可以得出,当n逐渐增大后,递归所花费的时间明显高于迭代的时间。
原因分析:
- 递归:
递归实现斐波那契数列的算法在计算过程中会重复计算很多次相同的子问题,导致时间复杂度高。
在递归实现中,每次递归调用时都会产生两个子问题,直到达到基本情况。因此,最好最坏情况下的时间复杂度为O(2^n)。这种情况发生在递归树的每一层都会有两个子问题产生,即递归深度为n。
平均情况:由于递归实现的时间复杂度是指数级的,平均情况下的时间复杂度也是O(2^n)。
- 迭代:
迭代实现斐波那契数列的算法则只需要计算一次每个数字,时间复杂度相对较低。
迭代实现中,我们使用一个循环来计算斐波那契数列,循环的迭代次数与n值相关。因此,迭代实现的时间复杂度为O(n)。
无论是最好情况还是最坏情况,迭代实现的时间复杂度都是O(n)。
下图是n和2^n复杂度算法效率对比
2^n | n | faster | |
n=10 | 1024 | 10 | 102.4 |
n=100 | 1.20E+30 | 100 | 1.20E+28 |
n=1000 | 1.07E+301 | 1000 | 1.07E+298 |
n=10000 | 1.99e3010 | 10000 | 1.99e3006 |