随便记录一次数据结构与算法的分析作业,内容为分析循环和递归实现输出1-N的正整数的对比。从时间和空间上分析了两种方式实现的递归方法和循环区别。
一、数据记录图表
二、分析
第一张图表制作时由于在打游戏(滑稽),所以中间部分的数据偏高且无规律,后半段是挂着跑了一个晚上的结果,后面几张图都是在没有运行其他CPU占用大的程序所记录的,都可以看出无论是递归的还是循环,在运行时间上都与数据量N成明显的线性关系(中间的突变后面分析)。
由于受电脑使用和电脑性能的限制,没有跑非常大的数据,最大只跑到了400w,在所测数据内,递归和循环的运行时间没有较大区别。
在表中可以看到有部分地方时间发生了突变,一开始我也不知道具体原因,后猜测时由于测试时时间过长,系统屏保息屏和我唤醒屏幕导致,所以有了第四张图表,该表是在不息屏的情况且把输出窗口置顶测得(中间的断崖时由于前部分在导出视频所致)。为了严谨,取后部分数据进行比较,具体数据如图
图中第一列为数据量,第二三列为用时,长的那张图为不关闭显示所用时间(单位:s)。
可以看出不关闭显示的情况下数据输出的时间明显大于关闭显示窗口输出时间,个人认为这应该是windows中刷新屏幕的机制吧,屏幕看不到的地方就不刷新吗,所以用时短。
接下来就是内存的使用了,最后一张图非常完美的一条直线不用多说了,递归的内存使用随数据量线性增加,而循环不存在返回上层调用的问题,也不会有线性增长的内存开销。
三、结论
在输出1-N的正整数的程序实现中,递归的方法和循环的方法在运行时间上没有明显区别,且都与数据量N成线性增加的关系,时间复杂度均为O(n)。但是递归在内存的使用量上与数据量也为线性增加关系,空间复杂度为O(n),循环则为O(1)。
总的来说这次试验还是挺有收获的,在之前有惯性思维递归肯定会比循环跑的慢,这次也算是验证了一下,不过递归的内存开销确实大,这大概就是为什么尽量要把递归方法改为循环实现吧,不过递归确实在一些问体的实现上较循环容易。
还有一个未解的疑问,望大佬指点:所有的测试刚开始跑第一个数据的时候时间总是会偏高,而且先跑哪个哪个就变高,图中也可以明显看出第一个数据很奇怪,不太明白为什么,请教请教大佬们!!!
以上所有均为个人见解,如有不足或错误,欢迎提问指正。
四、附上实验用源代码
#include <stdio.h>
#include <time.h>
#define N 20 //测试数据量
#define STEP 1000 //数据递增量
//计时参数
clock_t start, stop;
double dt1, dt2;
int *mem_start = NULL, *mem_stop = NULL, mem_used = 0, flag = 0; //记录递归内存使用的一些参数
//循环的实现方法
void print_n_loop(int n);
//两种递归的实现方法
void print_n_recu1(int begin, int n);
void print_n_recu2(int n);
int main()
{
getch(); //按任意键开始
//数据输出到文件
FILE *loop_time = fopen("loop_time_1.txt", "w+");
FILE *recu_time = fopen("recu_time_1.txt", "w+");
FILE *recu_mem = fopen("recu_mem_1.txt", "w+");
for (int i = 1; i <= N; i++)
{
start = clock();
print_n_loop(i * STEP);
stop = clock();
dt1 = (double)(stop - start) / CLK_TCK;
flag = 1;
start = clock();
//print_n_recu1(1, i * STEP);
print_n_recu2(i * STEP);
stop = clock();
dt2 = (double)(stop - start) / CLK_TCK;
mem_used = (int)(mem_start - mem_stop);
fprintf(loop_time, "%lf\n", dt1);
fprintf(recu_time, "%lf\n", dt2);
fprintf(recu_mem, "%d\n", mem_used);
}
fclose(loop_time);
fclose(recu_time);
fclose(recu_mem);
return 0;
}
void print_n_loop(int n)
{
for (int i = 1; i <= n; i++)
printf("%d\n", i);
}
void print_n_recu1(int begin, int n)
{
//内存计算 时间计算时注释
/*if (flag)
{
mem_start = &n;
flag = 0;
}
if (begin > n)mem_stop = &n;*/
if (begin > n)return;
printf("%d\n", begin);
print_n_recu1(++begin, n);
}
void print_n_recu2(int n)
{
//内存计算 时间计算时注释
/*if (flag)
{
mem_start = &n;
flag = 0;
}
if (!n)mem_stop = &n;*/
if (n)
{
print_n_recu2(n - 1);
printf("%d\n", n);
}
return;
}