递归是什么?
递归,顾名思义,就是递推和回归。
递归是一种解决问题的方法,在C语言中,递归就是函数自己调用自己。
#include <stdio.h>
int main()
{printf("hehe\n");main();//main函数中⼜调⽤了main函数return 0;
}
上面就是C语言最简单的递归代码。但是这种代码最终会陷入死递归,导致栈溢出(Stack overflow)。
递归的核心是思想和限制条件:
1、思想:把一个大型复杂的问题层层转化为一个与原问题相似,但规模较小的子问题来求解;直到子问题不能再拆分,递归就结束了。所以递归的思考方式就是把大事化小的过程。
2、递归在书写时,有了两个必要条件:一是递归要存在限制条件,当满足这个限制条件,递归结束。二是每次递归调用之后越来越接近这个限制条件,避免死递归。
递归举例
例1:求n的阶乘
运算过程:
当我们输出n为5之后,把5带入函数中,n为5,所以会返回5*Fact(4),而Fact(4)的值我们并不知道,同样需要带入计算,Fact(4)= 4 * Fact(3),依次进行下去,知到n = 0时返回1。我们的结果就是5*4*3*2*1*1 = 120.
#include <stdio.h>
int Fact(int n)
{if(n==0)return 1;elsereturn n*Fact(n-1);
}
int main()
{int n = 0;scanf("%d", &n);int ret = Fact(n);printf("%d\n", ret);return 0;
}
这里的n不能太大,否则会出现溢出。
例2:顺序打印⼀个整数的每⼀位
Print(n)如果 n 是 1234 ,那表⽰为 Print( 1234 ) // 打印 1234 的每⼀位其中 1234 中的 4 可以通过 % 10 得到。那么 Print( 1234 ) 就可以拆分为两步:1. Print( 1234 / 10 ) // 打印 123 的每⼀位2. printf ( 1234 % 10 ) // 打印 4完成上述 2 步,那就完成了 1234 每⼀位的打印那么 Print( 123 ) ⼜可以拆分为 Print( 123 / 10 ) + printf ( 123 % 10)
这样递推下去就能看出:Print( 1234 )==>Print( 123 ) + printf ( 4 )==>Print( 12 ) + printf ( 3 )==>Print( 1 ) + printf ( 2 )==> printf ( 1 )直到被打印的数字变成⼀位数的时候,就不需要再拆分,递归结束。
代码如下:
void Print(int n)
{if(n>9){Print(n/10);}printf("%d ", n%10);
}
int main()
{int m = 0;scanf("%d", &m);Print(m);return 0;
}
这里要再强调一下,上述 print 函数是当到达限制条件后递推结束,才开始回归,所以最后推出的1是先打印的。
递归与迭代
通过上面的举例,我们可以看出递归是一种很好的编程技巧,但是代码简洁的背后,是庞大的计算量。以代码举例1为例:Fact函数是可以产⽣正确的结果,但是在递归函数调⽤的过程中涉及⼀些运⾏时的开销。
在C语⾔中每⼀次函数调⽤,都要需要为本次函数调⽤在栈区申请⼀块内存空间来保存函数调⽤期间的各种局部变量的值,这块空间被称为运⾏时堆栈,或者函数栈帧。函数不返回,函数对应的栈帧空间就⼀直占⽤,所以如果函数调⽤中存在递归调⽤的话,每⼀次递归 函数调⽤都会开辟属于⾃⼰的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间。所以如果采⽤函数递归的⽅式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出(stack overflow)的问题。
所以如果不想使⽤递归就得想其他的办法,通常就是迭代的⽅式(通常就是循环的⽅式)。
⽐如:计算n的阶乘,也是可以产⽣1~n的数字累计乘在⼀起的。
int Fact(int n)
{int i = 0;int ret = 1;for(i=1; i<=n; i++){ret *= i;}return ret;
}
举例3:求第n个斐波那契数
#include <stdio.h>
int Fib(int n)
{if(n<=2)return 1;elsereturn Fib(n-1)+Fib(n-2);
}
int main()
{int n = 0;scanf("%d", &n);int ret = Fib(n);printf("%d\n", ret); return 0;
}
#include <stdio.h>
int Fib(int n)
{int a = 1;int b = 1;int c = 1;while(n>2){c = a+b;a = b;b = c;n--;}return c;
}
int main()
{int n = 0;scanf("%d", &n);
int ret = Fib(n);printf("%d\n", ret); printf("\ncount = %d\n", count);return 0;
}