【C语言】递归实战,通过几个例子带你深入走进递归算法

简介: 【C语言】递归实战,通过几个例子带你深入走进递归算法

一.什么是递归?

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

递归做为一种算法在程序设计语言中广泛应用。 递归算法通常指一个过程或函数在其定义或说明中直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。

递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。(也就是说我们现在学的需要使用递归的场景大多数通过循环也能解决,在下面介绍的例子中会用循环也实现一遍)

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

当你在使用递归时遇到思考瓶颈,请牢记大事化小的思想!!

具体有关递归知识的详解请看以下链接,这里我们是结合实战例子讲,就不在此具体展开了。

【C语言】带你玩转递归,迭代算法

实战一. 打印整型数据的每一位

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

例如:

输入:1234,输出 1 2 3 4


代码如下:

#include <stdio.h>
void print(int n)
{
if(n>9)//如果不大于9直接打印当前n的值即可
{
print(n/10);
}
printf("%d ", n%10);
}
int main()
{
int num = 1234;
print(num);
return 0;
}

我们想要把一个四位数甚至更多位的数每一位给剥离出来,首先要想到的是怎么得到每一位

我们知道,对10取余%就可以得到这个数的个位的数,而除以10就可以把这一位给消去,此时我们就可以这样实现(以下把n当作一个四位数举例)

先让n对10%得到此时这一位的数,然后将n/10来到下一位,再让n对10%得到这位数继续朝下进行直至n对10%等于0,说明此时n是个小于10的数只剩下它需要取了,取下它结束递归即可

画图解释分析

递归的递就是传递的意思,而归的意思是回归

fa73e68a67144951899245cbd43bb120.png

这就是递归!

解释完了递归在这段代码的应用,我们来讲讲这个代码中出现的问题

当我们中用户某天突发奇想输入了一个负数进去会发生什么呢?

61418d7a5d954fd185449c1edf39bda0.png

我们这个程序设定的条件是只针对正数的,要想输入一个负数也能打印就得这样修改代码

void print(int n)
{
  if (n > 9)//如果不大于9直接打印当前n的值即可
  {
    print(n / 10);
  }
  else if (n < -9)
  {
    print(n / 10);
  }
  printf("%d ", n % 10);
}
int main()
{
  int num = -1234;
  print(num);
  return 0;
}

或者把我们传入的int改为一个无符号整型

void print(unsigned int n)
{
  if (n > 9)//如果不大于9直接打印当前n的值即可
  {
    print(n / 10);
  }
  /*else if (n < -9)
  {
    print(n / 10);
  }*/
  printf("%d ", n % 10);
}

我在这里想提醒大家的是,无论在递归或者任何其他程序的编写中我们都得尽可能考虑各方面的情况,在以后的程序猿工作中,我们要知道用户是不总是照着你的指示来使用程序的,我们得尽可能保证程序的避免上面这种bug。

递归往往都能通过循环实现,我们现在来把这个例子用循环的方法实现一下

循环实现

//判断输入数字位数
#include<math.h>
int Strlen(unsigned int n)
{
  int count = 0;
  while (n / 10)
  {
    count++;
    n /= 10;
  }
  return count;
}
void print(unsigned int n)
{
  int i = 0;
  int ret = Strlen(n);
  if (n > 9)
  {
    for (i = ret; i > 0; i--)
    {
      int m = pow(10, i);
      printf("%d ", n / m);
      n %= m;
    }
  }
  printf("%d", n);
}
int main()
{
  int num = 123456789;
  print(num);
  return 0;
}

我们先来看看效果

a9d18c96e5df4d0aa5d31f050d5d31e4.png

实战二.汉诺塔问题

什么是汉诺塔?

汉诺塔(Tower of Hanoi)源自印度古老传说的一个游戏,大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,并且在移动时三根柱子之间一次只能移动一个圆盘。


从上面的对于汉诺塔问题的说明我们可以得到这样一个结论


如果要把A柱上的盘子全部都移动到C柱上,要遵循以下规则:


1.每次只能移动A柱最上面的一个盘子

2.小盘子上不能放大盘子。


我们来一步一步分析,首先设此时A中有n个盘子,我们现在的目标是借助B柱把A上的盘子全部移动到C柱上


思路分析:

当需要A上需要移动的盘子n=2时

我们只需要先把A最上面的盘子移到B上,再把A另一个盘子移到C上,最后把B上盘子移到C上即可。


当需要A上需要移动的盘子n=3时

将2个盘子从A移动到B上。重复n=2时的步骤,此时是将那2个盘子从A借助C移动到B上

接着,把A上的盘子移动到C上 ,将B上的2个盘子移动到C上。重复n=2时的步骤,此时是将那2个盘子从B借助A移动到C


当需要A上需要移动的盘子n=4时

将3个盘子从A移动到B上。重复n=3时的步骤,与n=3不同之处在于,我们此时是借助C把A上盘子移到B

将A上最后一个盘子移动到C上

将B上的3个盘子移动到C上。重复n=3时的步骤,与n=3不同之处在于,我们此时是借助A把B上盘子移到C


当需要A上需要移动的盘子为n时

怎么样,经过上面的讲解你发现规律了吗?

也就是说,对于任意一个大于1的正整数n,如果有一个n层汉诺塔的问题,我们就可以将之分解为两个n-1层汉诺塔问题求解


通过我们上面的分析,我们就可以把这个问题的解决分成这三步:


1.将A上n-1层的盘子通过C移动到B上

2.将此时A上剩余的盘子移动到C上

3.将B上此时n-1层的盘子通过A移动到C上


参考代码如下:

#include<stdio.h>
void Move (char A, char C, int n)
{
  printf("把第%d个盘子从%c--->%c\n", n, A, C);
}
void HanoiTower(char A, char B, char C, int n)
{
  if (n == 1)
  {
    Move(A, C, n);
  }
  else
  {
    //将n-1个盘子从A柱借助于C柱移动到B柱上
    HanoiTower(A, C, B, n - 1);
    //将A柱最后一个盘子移动到C柱上
    Move(A, C, n);
    //将n-1个盘子从B柱借助于A柱移动到C柱上
    HanoiTower(B, A, C, n - 1);
  }
}
int main()
{
  int n = 0;
  printf("输入A柱子上的盘子个数:");
  scanf("%d", &n);
  //将n个盘子从A柱借助于B柱移动到C柱上
  HanoiTower('A', 'B', 'C', n);
  return 0;
}


3ccaad70878c4a70a6f31865d966228a.png

实战三.青蛙跳台阶问题

青蛙跳台阶问题的描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个n级的台阶总共有多上种跳法。


青蛙跳台阶问题解题思路分析

当N=1时,那么青蛙就只有一种跳法。


当N=2时,青蛙可以跳两次一层台阶也可以跳一次二层台阶,有两种跳法。


当N=3时,青蛙可以先跳一次一层台阶,那么还需要跳两层台阶,那它此时就是N=2时的跳法,有两种跳法。

当青蛙跳一次二层台阶时,此时只需要跳一层台阶,那么它就是N=1时的跳法。

此时它的跳法就等于(N=1)+(N=2)种跳法。


当N=4时,青蛙跳一次一层台阶时,还需要跳三层台阶,那它此时剩下的跳法就等于N=3时的跳法,即有三种跳法。

青蛙跳一次二层台阶时,还剩二层台阶需要跳,那它此时剩下的跳法就是N=2时的跳法,则有两种跳法。

那么此时它的跳法就等于(N=2)+(N=3)种跳法。

思路总结

那么,不难看出青蛙跳台阶的规律,当N>2时,此时的跳法数就等于前面两个青蛙跳台阶跳法数之和


青蛙跳台阶代码实现

#include<stdio.h>
int Jump(int n)
{
  if (n == 1)
  {
    return 1;//当只有一层台阶时直接返回1
  }
   if (n == 2)
  {
    return 2;//当只有2层台阶时就返回2
  }
   if (n > 2)
   {
     return Jump(n - 1) + Jump(n - 2);
   }//当n>2时,利用递归进行返回
}
int main()
{
  int n = 0;
  scanf("%d", &n);
  int num = Jump(n);
  printf("%d\n", num);
  return 0;
}

以上的汉诺塔与青蛙跳台阶的思路分析这块由于篇幅原因都是简单的分析了一下,详细的可以看看下面我的这两篇博客:

【C语言】图文解析,深入浅出汉诺塔问题

【C语言】手把手带你解决青蛙跳台阶问题

总结

今天的内容暂时到这里就结束了,我们今天通过几个实战例子带大家了解了递归的应用,我希望你如果看懂了能够自己独立的实现一下,这样才叫真正的学会了。

好了,如果你有任何疑问欢迎在评论区或者私信我提出,大家下次再见啦!

新人博主创作不易,如果感觉文章内容对你有所帮助的话不妨三连一下这个新人博主再走呗。你们的支持就是我更新的动力!!!

**(可莉请求你们三连支持一下博主!!!点击下方评论点赞收藏帮帮可莉吧)**


20fa3306e76244de9879742c165c792a.gif

目录
相关文章
|
1月前
|
存储 编译器 C语言
爱上C语言:函数递归,青蛙跳台阶图文详解
爱上C语言:函数递归,青蛙跳台阶图文详解
|
29天前
|
算法
算法系列--递归(一)--与链表有关(上)
算法系列--递归(一)--与链表有关
28 0
|
1天前
|
存储 Shell Linux
操作系统实战(一)(linux+C语言)
本篇文章重点在于利用linux系统的完成操作系统的实验,巩固课堂知识
|
4天前
|
Arthas 监控 算法
JVM工作原理与实战(二十五):堆的垃圾回收-垃圾回收算法
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了垃圾回收算法评价标准、标记清除算法、复制算法、标记整理算法、分代垃圾回收算法等内容。
18 0
JVM工作原理与实战(二十五):堆的垃圾回收-垃圾回收算法
|
4天前
|
C语言
C语言实战演练之游戏框架
C语言实战演练之游戏框架
|
5天前
|
存储 缓存 算法
【C 言专栏】C 语言实现算法的高效性
【5月更文挑战第6天】本文探讨了C语言在实现高效算法上的优势,包括其高效性、灵活性、可移植性和底层访问能力。关键点包括选择合适的数据结构(如数组、链表、树和图)、应用优化策略(如减少计算、空间换时间、分治和动态规划),以及内存管理和代码优化技巧。通过实际案例(如排序和图遍历算法),阐述了如何利用C语言实现算法高效性,并强调在实践中不断探索和优化以提升算法效率。C语言在计算机科学中的重要地位使其成为实现高效算法的首选工具。
【C 言专栏】C 语言实现算法的高效性
|
5天前
|
搜索推荐 C语言
【C语言/数据结构】排序(归并排序|计数排序|排序算法复杂度)
【C语言/数据结构】排序(归并排序|计数排序|排序算法复杂度)
11 0
|
5天前
|
C语言
【C语言/数据结构】排序(快速排序及多种优化|递归及非递归版本)
【C语言/数据结构】排序(快速排序及多种优化|递归及非递归版本)
7 0
|
9天前
|
机器学习/深度学习 自然语言处理 算法
机器学习算法原理与应用:深入探索与实战
【5月更文挑战第2天】本文深入探讨机器学习算法原理,包括监督学习(如线性回归、SVM、神经网络)、非监督学习(聚类、PCA)和强化学习。通过案例展示了机器学习在图像识别(CNN)、自然语言处理(RNN/LSTM)和推荐系统(协同过滤)的应用。随着技术发展,机器学习正广泛影响各领域,但也带来隐私和算法偏见问题,需关注解决。
|
10天前
|
机器学习/深度学习 算法 C语言
【C言专栏】递归算法在 C 语言中的应用
【4月更文挑战第30天】本文介绍了递归算法在C语言中的应用,包括基本概念(通过调用自身解决子问题)、特点(调用自身、终止条件、栈空间)和实现步骤(定义递归函数、分解问题、设置终止条件、组合解)。文中通过阶乘计算和斐波那契数列两个案例展示了递归的使用,强调了递归可能导致的栈溢出问题及优化需求。学习递归有助于理解和应用“分而治之”策略。