C语言——递归 (中)

简介: C语言——递归 (中)

练习1 : 接受一个整型值,按照顺序打印它的每一位  (画图讲解)

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


例如︰输入∶1234,输出1234.

要顺序打印它的每一位,就需要得到它的每一位,1234最容易得到的就是个位

1234 % 10 = 4

1234 / 10 = 123 % 10 = 3

123 / 10 = 12 % 10 = 2

12 / 10 = 1 % 10 = 1

1 / 10 = 0

#include<stdio.h>
void print(int n)
{
  if (n > 9)
  {
    print(n / 10);
  }
  printf("%d ", n % 10);
}
int main()
{
  unsigned int num = 0;
  scanf("%d", &num);//1234
  print(num);//打印1 2 3 4
    return 0;
  //print(1234)
  //print(123) 4
  //print(12) 3 4
  //print(1) 2 3 4
  //      1   2  3 4  
  //当()里面的数字大于9的时候就拆分,小于9,为个位数的时候停止拆分,进行打印
}

image.png


比较上面两个递归函数,我们可以看到: 递归的两个必要条件

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

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


注意:这两个条件是必要条件,不是充分条件,也就是说递归函数一定满足这两个条件,但是满足这两个条件不一定是递归。

看下面这个例子:


image.png


按F10进行调试:

image.png


每一次函数的调用,都需要在栈区分配一定的空间,调用次数太多,栈空间不够分配(被耗干),导致栈溢出。


所以我们在写递归代码的时候,一定要注意以下几点:

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

2、递归层层不能太深


练习 2∶ 求字符串的长度(画图讲解)

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

如果调用临时变量:如果用strlen算:

image.png


使用递归如下⬇⬇⬇⬇⬇⬇

初步解题思路:用了临时变量count

#include<stdio.h>
#include<string.h>
//求字符串长度
int my_strlen(char* pr)
{
  int count = 0;
  while (*pr != '\0')
  {
    count++;
    pr++;
  }
  return count;
}
int main()
{
  char arr1[] = "bit";
  //int len = strlen(arr1);//求字符串长度
  int len = my_strlen(arr1);
  //arr1是数组,数组传参,传过去的不是整个数组,而是首元素的地址
  printf("%d\n", len);
  return 0;
}

image.png


这种方式虽然计算出字符串的长度,但是创建了一个临时变量count,现在使用递归的方式来实现:

int my_strlen(char* pr)
{
  if (*pr != '\0')
    return 1 + my_strlen(pr + 1);
  else
    return 0;
}
//把大事化小
//my_strlen("bit")
//1+my_strlen("it")
//1+1+my_strlen("t")
//1+1+1+my_strlen("\0")
//1+1+1+0    = 3

image.png


画图详解:

image.png


练习3 ∶ 求n的阶乘

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

#include<stdio.h>
#include<string.h>
int Fun1(int x)//迭代(循环)实现
{
  int i = 0;
  int y = 1;
  for (i = 1; i <= x; i++)
  {
    y = y * i;
  }
  return y;
}
int Fun2(int x)//递归实现
{
  if (x > 1)
    return x * Fun2(x - 1);
  else
    return 1;
}
int main()
{
  int n = 0;
  int ret = 0;
  printf("请输入一个数:>>\n");
  scanf("%d", &n);
  //ret = Fun1(n);//循环的方式
  ret = Fun2(n);//递归的方式
  printf("%d的阶乘为:%d\n", n, ret);
  return 0;
}

image.png


练习4 ∶ 求第n个斐波那契数 (不考虑溢出)

求第n个斐波那契数。(不考虑溢出)


介绍斐波那契数列,斐波那契数列的排列是:1 , 1,2,3,5,8,13,21,34,55,89,......

依次类推下去,你会发现,它后一个数等于前面两个数的和。

#include<stdio.h>
int count = 0;
int Fib1(int x)//递归实现
{
  if (x == 3)
    count++;
  if (x <= 2)
    return 1;
  else
    return Fib1(x - 1) + Fib1(x - 2);
}
int Fib2(int x)//函数实现
{
  int a = 1;
  int b = 1;
  int c = 1;
  while (x > 2)
  {
    c = a + b;
    a = b;
    b = c;
    x--;
    count++;
  }
  return c;
}
int main()
{
  int n = 0;
  int ret = 0;
  scanf("%d", &n);
  //ret = Fib1(n);//求第n个斐波那契数
  ret = Fib2(n);//循环实现
  printf("%d\n", ret);
  printf("count = %d\n", count);
  return 0;
}

我们在主函数里面写后续要被调用的某个函数的时候,先假设要用这个函数实现什么功能,直接去用,之后再去真正定义并实现这个函数。

这种思想叫做:TDD - 测试驱动开发 先去想函数怎么用,然后再实现。


递归结果:

image.png


递归方式:效率低

image.png


循环结果:


image.png


可以看出此时使用递归方式并不高效,其计算同一个数比如 3 的次数为 2 ^ n (递归方式)

而使用循环方式(n > 3时) 次数为 n


那我们如何改进呢 ?

在调试factorial函数的时候,如果你的参数比较大,那就会报错 : stack overflow(栈溢出)这样的信息。系统分配给程序的栈空间是有限的,但是如果出现了死循环,或者(死递归),这样有可能导致一直开辟栈空间,最终产生栈空间耗尽的情况,这样的现象我们称为栈溢出。

那如何解决上述的问题 ?

1.将递归改写成非递归。

⒉使用static对象替代nonstatic局部对象。在递归函数设计中,可以使用static对象替代nonstatic局部对象(即栈对象),这不仅可以减少每次递归调用和返回时产生和释放nonstatic对象的开销,而且static对象还可以保存递归调用的中间状态,并且可为各个调用层所访问。


2、递归练习:

1、字符串逆序:

编写一个函数 reverse_string(char* string)(递归实现)

实现:将参数字符串中的字符反向排列,不是逆序打印。

要求:不能使用C函数库中的字符串操作函数。

比如 : char arr[] = “abcdef”,逆序之后数组的内容变成:fedcba


#include<stdio.h>
//#include<string.h>
//int my_strlen(char* str)//非递归方法求字符串长度
//{
//  int count = 0;
//  while (*str != '\0')
//  {
//    count++;
//    str++;
//  }
//  return count;
//}
int my_strlen(char* str)//递归方法求字符串长度
{
  if (*str != '\0')
    return 1 + my_strlen(str + 1);
  else
    return 0;
}
//void reverse_string(char* str)//非递归方法逆序字符串
//{
//  //int len = strlen(str);
//  int len = my_strlen(str);
//  int left = 0;
//  int right = len - 1;
//  while (left < right)
//  {
//    char tmp = *(str+left);
//    *(str + left) = *(str + right);
//    *(str + right) = tmp;
//    left++;
//    right--;
//  }
//
//}
void reverse_string(char* str)
{
  int len = my_strlen(str);
  if (len > 1)
  {
    char tmp = *str;
    *str = *(str + len - 1);
    *(str + len - 1) = '\0';
    reverse_string(str + 1);
    *(str + len - 1) = tmp;
  }
}
int main()
{
  char arr[] = "abcdefjh";
  printf("逆序前:%s\n", arr);
  reverse_string(arr);
  printf("逆序后:%s\n", arr);
  return 0;
}

image.png



目录
相关文章
|
7天前
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
46 16
|
5月前
|
机器学习/深度学习 C语言
九/十:《初学C语言》— 扫雷游戏实现和函数递归基础
【8月更文挑战第5天】本篇文章用C语言采用多文件编写实现了一个基础的扫雷游戏(附源码),并讲解了关于函数递归的基础概念及其相对应的习题练习(附源码)
51 1
九/十:《初学C语言》— 扫雷游戏实现和函数递归基础
|
3月前
|
机器学习/深度学习 C语言
【c语言】一篇文章搞懂函数递归
本文详细介绍了函数递归的概念、思想及其限制条件,并通过求阶乘、打印整数每一位和求斐波那契数等实例,展示了递归的应用。递归的核心在于将大问题分解为小问题,但需注意递归可能导致效率低下和栈溢出的问题。文章最后总结了递归的优缺点,提醒读者在实际编程中合理使用递归。
95 7
|
3月前
|
C语言
c语言回顾-函数递归(上)
c语言回顾-函数递归(上)
56 2
|
3月前
|
C语言
c语言回顾-函数递归(下)
c语言回顾-函数递归(下)
60 0
|
5月前
|
机器学习/深度学习 C语言
【C语言篇】递归详细介绍(基础概念习题及汉诺塔等进阶问题)
要保持最小的步数,每一次汉诺塔问题(无论是最初还是递归过程中的),如果此时初始柱盘子数为偶数,我们第一步是把最上面的盘子移动到中转柱,如果为奇数,我们第一步则是将其移动到目标柱。
125 0
【C语言篇】递归详细介绍(基础概念习题及汉诺塔等进阶问题)
|
5月前
|
C语言
C语言中的递归
C语言中的递归
|
6月前
|
存储 编译器 C语言
|
5月前
|
算法 编译器 C语言
【C语言】递归
【C语言】递归
30 0
|
7月前
|
机器学习/深度学习 算法 C语言
详细介绍递归算法在 C 语言中的应用,包括递归的基本概念、特点、实现方法以及实际应用案例
【6月更文挑战第15天】递归算法在C语言中是强大力量的体现,通过函数调用自身解决复杂问题。递归涉及基本概念如自调用、终止条件及栈空间管理。在C中实现递归需定义递归函数,分解问题并设定停止条件。阶乘和斐波那契数列是经典应用示例,展示了递归的优雅与效率。然而,递归可能导致栈溢出,需注意优化。学习递归深化了对“分而治之”策略的理解。**
151 7

热门文章

最新文章