尾递归:
即 写成尾调用形式的递归。
尾递归的结果只跟传入的参数有关,因此只需要用少量的变量记录参数变化,便能够轻易的改写成循环形式,
所以尾递归和循环等价,也即和迭代等价。
递归 与 迭代 的关系:
递归:自己调用自己
迭代:A不停地调用B
递归中一定有迭代,
迭代中不一定有递归,
大部分情况下可以相互转化,
又因为递归浪费空间易造成栈溢出,所以能用迭代的就不用递归
//这是递归
int funcA(int n)
{
if(n > 1)
return n+funcA(n-1);
else
return 1;
}
//这是迭代
int funcB(int n)
{
int i,s=0;
for(i=1;i<n;i++)
s+=i;
return s;
}
尾调用:
无需返回的函数调用,即函数调用不处于参数的位置上。
Add(1,2), Mul(1,2)是尾调用;
Add(1, Mul(3,4))不是尾调用,因为函数Mul作为了函数Add的参数,
在计算Mul(3,4)之前需要保存状态,待Mul算完后把返回结果result送给Add计算Add(1,result)。
写成尾调用的形式 利于 编译器对函数调用 进行优化。
只要编译器判断某条语句是尾调用,编译器就不会保存状态。
代码——阶乘的3种实现方式
int factorial(int n,int count)// count:counter
{
printf("%d --------- ",count);
if(n<0){
return -1;
}else if(n==0 || n==1){
return 1;
}else{
count++;
return n*factorial(n-1,count);
}
}
int tailed_factorial(int number, int result)
{
if (number < 0)
return -1;
else if (number == 0)
return 1;
else if (number == 1)
return result;
else
return tailed_factorial(number - 1, number * result);//在函数的参数上实现的乘法运算
}
int iteration_factorial(int number)
{
int result = 1;
if(number < 0){
return -1;
}else if(number == 0 || number == 1){
return 1;
}else{
for(int i=1;i<=number;i++){
result = result * i;
}
}
return result;
}
递归过程的说明:
( Factorial 3 )
= ( 3 * ( Factorial 2 ) )
= ( 3 * ( 2 * ( Factorial 1 ) ) ),这需要用到3层栈结构
= ( 3 * ( 2 * 1 ) )
= ( 3 * 2 )
= 6
每个活跃期 计算 n倍的(n-1)!,
让n = n-1,持续这个过程直到n = 1为止。
每个活跃期的返回值都依赖于用n乘以下一个活跃期的返回值,
所以每次调用产生的栈帧必须保存在栈上,直到下一个调用的返回值确定。
尾递归过程的说明:
( TailedFactorial(3,1) )
= ( TailedFactorial(2,3 * 1) )
= ( TailedFactorial(1,2 * 3 * 1) )
= 2 * 3 * 1
实现上述过程最少可以用到1层栈结构,
而现代的编译器可以做到当检测到一个函数是尾递归的时候,
它覆盖当前的活动记录即栈帧,而不是在其之上重新添加一个新的,这样就只用到1层栈。
这样使得栈空间大大缩减,使得实际的运行效率变得更高。
代码——斐波那契数列的3种实现方式
// 0,1,1,2,3,5,8,13......
int fib(n)
{
if(n < 0){
return -1;
}else if(n==0 || n==1){
return n;
}else{
return fib(n-2)+fib(n-1);
}
}
int tailed_fib(int first, int second, int arr_index, int result)
{
if (arr_index < 0)
return -1;
else if (first ==0 && second == 1 && (arr_index == 0 || arr_index == 1))
return arr_index;
else if (arr_index == 1)
return result;
else
return tailed_fib(second,first+second,--arr_index,first+second);//在函数的参数上实现的加法运算
}
int iteration_fib(int first,int second,int arr_index)
{
if (arr_index < 0)
return -1;
else if (first ==0 && second == 1 && (arr_index == 0 || arr_index == 1))
return arr_index;
else{
int result = 0;
for(int i=arr_index;i>1;i--){
result = first + second;
first = second;
second = result;
}
return result;
}
}
int corrected_tail_recursive_fib(int first, int second, int arr_index, int result)// should set result = 0
{
if (arr_index < 0)
return -1;
else if (arr_index == 0){
//printf("**\n");// don't execute
return first;
}
else if (arr_index == 1){
//printf("##\n");// don't execute
return second;
}
else // arr_index > 1
return tail_recursive_fib(second,first+second,--arr_index,first+second);//在函数的参数上实现的加法运算
}
/*
first = 3, second = 5, arr_index = 4, result = 0
tail_recursive_fib(5,3+5,3,3+5)
tail_recursive_fib(3+5,(3+5)+5,2,(3+5)+5)
tail_recursive_fib((3+5)+5,((3+5)+5)+(3+5)),1,((3+5)+5)+(3+5)))
return ((3+5)+5)+(3+5))
*/
// array index: 0,1,2,3, 4, 5, 6, 7, 8, 9
// array value: 0,1,1,2, 3, 5, 8, 13, 21, 34 ......
// array value: 3,5,8,13,21,34,55,89,144,233......
int corrected_iterative_fib(int first,int second,int arr_index)
{
if (arr_index < 0)
return -1;
else if (arr_index == 0)
return first;
else if (arr_index == 1)
return second;
else{
int result = 0; // 这种方法是在 这个地方 对result设定初始值0
for(int i=arr_index;i>1;i--){
result = first + second;
first = second;
second = result;
}
return result;
}
}
int main()
{
int first,second,array_index;
printf("please input the value of var first,second,array_index\n");
scanf("%d%d%d",&first,&second,&array_index);
printf("corrected tail recursive Fibonacci result is %d\n",corrected_tail_recursive_fib(first,second,array_index,0));// let result = 0
printf("corrected iterative Fibonacci result is %d\n",corrected_iterative_fib(first,second,array_index));
}