3.6 函数的递归

    • 什么是递归

程序调用自身的编程技巧称为递归( recursion)。(函数自己调用自己)

递归做为一种算法在程序设计语言中广泛应用。

一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。

递归的主要思考方式在于:把大事化小

2. 递归的两个必要条件

  • 存在限制条件,当满足这个限制条件的时候,递归便不再继续。

  • 每次递归调用之后越来越接近这个限制条件。

3. 练习

①.简单递归举例

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    printf("hehe\n");
    main();
    return 0;
}

上面程序main()自己调用自己,属于递归,但为是个死循环。后面会跳出栈溢出的错误提示

注意:

  1. 不能死递归,需要有跳出条件,每次递归逼近跳出条件

  1. 递归层次不能太深,不然也可能栈溢出

②. 接受一个整型值(无符号),按照顺序打印它的每一位。

例如: 输入:1234,输出 1 2 3 4


不使用递归时:

可采用模除的方法:模除:得到的是一个数除以另一个数的余数

1234%10= 4

1234/10=123 123%10=3

123/10=12 12%10=2

12/10=1 1%10=1

1/10=0

当结果变为0的时候,说明每一位都被拿下来了

但结果为4321,我们需要打印1234,此时我们可以将结果存到数组中,倒着打印出来。


采用递归的方法时:

假设用print(1234)打印1 2 3 4的每一位

可将其拆分为先打印print(123)的每一位,再打印4

print(123)可拆分为print(12),再打印3 4

print(12)可拆分为print(1),再打印 2 3 4

print(1)不用拆,也就是说num<9时,等价于num为个位数时,不用拆分,大于9时才需要拆分。

print(1234)

print(123) 4

print(12) 3 4

print(1) 2 3 4


#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void print(unsigned int n)
{
    if (n > 9) //递归限制条件,控制递归啥时候进去啥时候出去  不是个位数的时候进去拆分
    {
        print(n / 10);  //使得每次递归调用之后越来越接近限制条件   eg 1234/10 =123   此时n为123
    }
    printf("%d ", n % 10);//1234%10=4 打印出4  
}
int main(void)
{
    unsigned int num = 0;
    scanf("%u", &num);  //1234
    //递归——函数自己调用自己
    print(num); //p函数可以打印参数部分数字的每一位

    return 0;
}
C语言 函数递归,通过练习了解递归调用的详细过程。接受一个整型值(无符号),按照顺序打印它的每一位。例如:输入:1234,输出 1 2 3 4。_食人的小草的博客-CSDN博客

③. 编写函数不允许创建临时变量,求字符串的长度

使用库函数strlen()求字符串长度

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main(void)
{
    char arr[] = "bit";
    //对于数组arr来说,我们得知道它里面放了下面四个元素:
    //['b'] ['i'] ['t'] ['\0']  
    //['\0']是字符串结束标志,计算长度的时候不算它
    printf("%d\n", strlen(arr));  //输出为3
    return 0;
}

模拟实现一个strlen()函数

// 模拟实现一个strlen()函数
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int my_strlen(char* str)
{
    int count = 0;
    while (*str != '\0')  //*str 是指通过str指针拿到数组里的第一个字符
    {
        count++;
        str++;
    }
    return count;
}
int main()
{
    char arr[] = "bit";

    //模拟实现一个strlen()函数
    printf("%d\n", my_strlen(arr));
    //arr是数组名,数组在传参的时候,传的是数组首元素的地址,函数定义的时候对应参数处应该写指针
    //因为计算的是长度,所以my_strlen()函数返回类型是int
    return 0;
}

不创建临时变量怎么做?

大事化小思路:

my_strlen("bit");

//my_strlen求字符串“bit”的长度,my_strlen要拿到字符串第一个字符的地址很简单,*str 拿到的就是第一个字符的地址,这时候可以判断这个字符是不是“\0”,如果不是,就可分解为:

1 + my_strlen("it");

//将取得的 1 剥离下来,继续去求字符串“it" 的长度,同样*str 拿到的就是第一个字符的地址,这时候可以判断这个字符是不是“\0”,如果不是,就可以分解为:

1 + 1 +my_strlen("t");

//同理,分解为:

1 + 1 +1+my_strlen("");

//此时,my_strlen函数一看,已经读到“\0”了,已经没有东西了,所以长度是0,所以:

④. 求n的阶乘(不考虑溢出)

不考虑递归

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    int n = 0;
    scanf("%d", &n);
    int i = 0;
    int ret = 1;
    //循环是一种迭代的方式
    for (i = 1; i <= n; i++)
    {
        ret = ret * i;
    }
    printf("%d", ret);
    return 0;
}

n! = 1*2*3....*n

| -----》 n<= 1, 返回1

fac(n) |

| -----》 n>1, 返回 n*fac(n-1)


采用递归的方法

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int fac(int n)
{
    if (n <= 1)
        return 1;
    else
        return n * fac(n - 1);
}
int main(void)
{
    int n = 0;
    scanf("%d", &n);
    int ret = fac(n);
    printf("%d\n", ret);
    return 0;
}

⑤. 求第n个斐波那契数。

斐波那契数列:前两个之和等于第三个数列

1 ,1,2,3,5,8,13,21........

| <=2, 1

n |

| >2, fib(n - 1) + fib(n - 2)

如果求第50个斐波那契数,程序会运行很久,因为计算机要进行重复大量的计算,效率太低。

可以采用迭代的方式,效率非常高。

递归和迭代的区别:递归和迭代都是循环的一种。. 递归是重复调用函数自身实现循环。迭代是函数内某段代码实现循环。

#define _CRT_SECURE_NO_WARNINGS 
#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--;  //n不减的话就死循环了
    }
    return c;
}
int main(void)
{
    int n = 0;
    scanf("%d", &n);
    int ret = fib(n);
    printf("%d\n", ret);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值