递归是数据结构里常见的,递归调用是通过内存的栈实现的,在调用函数时,系统会将被调函数所需的程序空间安排在一个栈中。每当调用一个函数时,就在栈顶为它分配一个存储区。每当从一个函数退出时就释放它的存储区,栈是采用先进后出(后进先出),当递归不断调用自己,最先调用的先入栈,后面依次调用的函数依次进入栈,最先入栈的位于栈底,最后入栈的栈顶,比如我们穿衣服,最先穿的内衣位于最里面,最后穿的外套位于最外面,脱衣服的最先脱外套,然后依次脱里面的,直至脱光。整个递归调用过程就是一次穿衣脱衣(入栈出栈)的过程。由于递归调用要有大量的函数调用,这也有许多额外的时间开销。函数调用要发送实参,要为被调函数分配存储空间,还要保存返回的值。如果递归调用次数多,导致系统开销太大。这个时候递归就不是好的选择,我们尽量采用循环来实现。
我们来看个例子:斐波那契数列,每一个数值都是其前两个数值之和
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
我们对比递归和循环看看效率
递归:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_FBNC 30
long iFibonacci;
long fibonacci(int num)
{
iFibonacci++;
int iResult;
if(num < 0)
return -1;
if(num == 0){
return 0;
}else if(num == 1){
return 1;
}
else{
return fibonacci(num-1)+fibonacci(num-2);
}
}
int main(int argc,char *argv[])
{
for(int i=0;i<=MAX_FBNC;i++){
iFibonacci = 0;
printf("%d的Fibonacci:[%ld]\n",i,fibonacci(i));
printf("调用次数:[%d]\n\n",iFibonacci);
}
}
运行结果:
我们发现到第30个数时,函数被调用了2692537次,造成了巨大的资源浪费。
循环:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_FBNC 40
long fibonacci_loop(int num)
{
int iResult=0;
int *a=malloc(sizeof(int)*num);
for(int i=0;i<num;i++){
if(i==0)
a[i]=0;
else if(i==1)
a[i]=1;
else
a[i]=a[i-1]+a[i-2];
}
iResult = a[num-1];
free(a);
return iResult;
}
int main(int argc,char *argv[])
{
for(int i=0;i<=MAX_FBNC;i++){
printf("%d的fibonacci_loop:[%ld]\n",i,fibonacci_loop(i+1));
}
}
每次计算只调用一次函数,资源开销大大减小,时间复杂度为O(n),效率大大提高。
既然递归的效率远远不如循环,能用循环就用循环,虽然递归的程序非常便于理解。在一些递归调用次数少,便于理解的情况下,可以用递归。例如十进制转换成二进制、十六进制、八进制:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char hex_char(int num);
void binary(int num)
{
int yushu;
yushu = num%2;
if(num>=2)
binary(num/2);
printf("%d",yushu);
}
void hex(int num)
{
int yushu;
yushu = num%16;
if(num>=16)
binary(num/16);
printf("%c",hex_char(yushu));
}
void octonary(int num)
{
int yushu;
yushu = num%8;
if(num>=8)
octonary(num/8);
printf("%d",yushu);
}
char hex_char(int num)
{
char cRusult;
switch(num){
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9: cRusult=num+0x30; break;
case 10: cRusult='A'; break;
case 11: cRusult='B'; break;
case 12: cRusult='C'; break;
case 13: cRusult='D'; break;
case 14: cRusult='E'; break;
case 15: cRusult='F'; break;
}
return cRusult;
}
int main(int argc,char *argv[])
{
int a = 29;
printf("a:[%d]\n",a);
printf("binary num is:");
binary(a);
fflush(stdout);
printf("\n");
printf("hex num is:");
hex(a);
fflush(stdout);
printf("\n");
printf("octonary num is:");
octonary(a);
fflush(stdout);
printf("\n");
}