笔记函数递归

递归介绍:

        递归其实是⼀种解决问题的方法,在C语言中,递归就是函数自己调用自己。
可以看一个简单的递归程序,只是为了演示递归的基本形式,不是为了解决问题,代码最终也会陷⼊死递归,导致栈溢出(Stackoverflow)

#include <stdio.h>

int main()
{
    printf("hehe\n");
    main();//main函数中⼜调⽤了main函数
    return 0;
}

        递归的思想:递归中的递就是递推的意思,归就是回归的意思,其实把⼀个⼤型复杂问题层层转化为⼀个与原问题相似,但规模较小的子问题来求解;直到子问题不能再被拆分,递归就结束了。所以递归的思考⽅式就是把⼤事化小的过程。

        递归有两个限制条件
1、递归存在限制条件,当满足这个限制条件的时候,递归便不再继续。
2、每次递归调用之后越来越接近这个限制条件

        例1:求n的阶乘(⼀个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1。自然数n的阶乘写作n!)
        题目:计算n的阶乘(不考虑溢出),n的阶乘就是1~n的数字累积相乘。

        思路:n! = n ∗ (n - 1)! 
        把⼀个较大的问题,转换为⼀个与原问题相似,但规模较小的问题来求解的。n的阶乘和n-1的阶乘是相似的问题,但是规模变小了
下列是n的阶乘的递归公式:

        假设Fact(n)就是求n的阶乘,那么Fact(n-1)就是求n-1的阶乘,此时Fact(n)==n*Fact(n-1),依次把Fact()函数表示出来,递归存在限制条件,n=0不递归,每次递归调用后越来越接近限制条件
n在不断变小4、3、2、1,最后变成0,慢慢接近跳出条件n==0,即Fact(0)==1时逐渐回归
把一个相对大的任务拆分了每次递归调用后越来越接近限制条件,求直到最后被限制条件

#include<stdio.h>
Fact(n)
{
	if (n == 0)
		return 1;
	else
		return n * Fact(n - 1);
}


int main()
{
	int n = 0;
	scanf("%d", &n);
	int r = Fact(n);
	printf("%d", r);
	return 0;
}

 

      递归函数调用的过程中涉及⼀些运行时的开销。
      每⼀次函数调用,都需要为本次函数调用在内存的栈区,申请⼀块内存空间来保存函数调用期间的各种局部变量的值,这块空间被称为运行时堆栈,或者函数栈帧。
        函数不返回,函数对应的栈帧空间就⼀直占⽤,所以如果函数调⽤中存在递归调⽤的话,每⼀次递归函数调用都会开辟属于自己的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间
        因此时先创建再销毁,每次函数递归调用函数在栈区申请一块内存空间,即先申请栈帧空间,最后把栈帧空间还给操作系统,栈帧空间也不是无限的,如果递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出(stack overflow)

        

        例2:顺序打印⼀个整数的每⼀位
        输入⼀个整数m,按照顺序打印整数的每⼀位。比如: 输入:1234输出:1 2 3 4
 输入:520输出:5 2 0

        思路:如果n是⼀位数,n的每⼀位就是自己,n是超过1位数的话,就得拆分每⼀位
        1234%10就能得到4,然后1234/10得到123,这就相当于去掉了4。
        同理可得123/10得12,123%10得3
        12/10得1,12%10得2
        不过问题变成了得到数字顺序是倒着的
        想法为写⼀个函数Print来打印n的每⼀位,先递归,再回推
        Print(1234)拆成两步,Print(1234/10)和printf(1234%10),也就是n>9时可以一直拆分,直到限制条件为被打印的数字变成⼀位数的时候即为n<10,就不需要再拆分,Print()函数递归结束。最后回归按照反过来的顺序打印每一位余数。

        

#include<stdio.h>
int Print(int n)
{
	if (n > 9)
	{
		Print(n / 10);
	}
	printf("%d ", n % 10);

}

int main()
{
	int n = 0;
	scanf("%d", &n);
	Print(n);
	return 0;
}

        

        如果一个算法若用递归方式写简单了但有明显的缺陷,需用迭代的方式(通常就是循环的方式),难写也得给他写出来,此时迭代实现比递归实现效率更高。
        如果一个算法用递归的方式去写非常的快捷方便,用迭代的方式复杂同时在递归完后无明显的缺陷。可用递归的方式写,因为它比非递归的形式更加清晰

        例3:求第n个斐波那契数(前两个数一加=第三个数)

这公式,很容易诱导我们将代码写成递归的形式

#include <stdio.h>
int Fib(int n)
{
    if(n<=2)
        return 1;
    else
        return Fib(n-1)+Fib(n-2);
}

int main()
{
    int n = 0;
    scanf("%d", &n);
    int ret = Fib(n);
    printf("%d\n", ret);
    return 0;
}


实际上n的值越大,需要很长时间才能算出结果,递因为归程序会不断的展开,在展开的过程中,会有重复计算,而且递归层次越深,冗余计算就会越多比如n输⼊为50的时候


        

        此时计算是非常冗余的。所以斐波那契数的计算,使用递归是非常不明智的,我们就得想迭代的方式解决
        思路为假设第1、2、3个数为a,b,c
        第一次a=1,b=1,c=a+b,
        第二次a要等于第一次的b了,即a=b,    b=第1次的c,b=c;即c= 第二次a+b,依次循环计算
 

#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);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值