目录

序言:

一.函数递归( recursion)

二.递归的两个必要条件

三.递归小问题

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

(2)编写函数不允许创建临时变量,求字符串的长度(利用递归求解)

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

提示:1. 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。

四.经典递归案例

1.汉诺塔

2.青蛙跳台阶

最后:


序言:

今天来聊一聊递归,相信函数递归是不少小伙伴在学习编程路上遇到的一个拦路虎吧,今天带着大家一起把这个拦路虎干掉。相信小伙伴跟着我一起把递归深度解剖一遍,大家一定会内功大增。

一.函数递归( recursion)

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

递归的主要思考方式在于:把大事化小,小问题也是大问题的一个子问题,只要将子问题解决大问题也就迎刃而解。

二.递归的两个必要条件

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

注意:存在这两个条件递归不一定正确,但是不存在这两个条件,递归一定不正确。

三.递归小问题

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

我们要想输出他的每一位,是不难的,直接用循环模10除10并将得到的数,存储到数组中,然后倒叙输出,就解决问题了。但是这里我们希望使用递归来解决问题。又该怎么理解呢?

首先分析问题,我们需要正序打印每一位,我们可以先打印 123  加上单独 4 这样就把问题分成一个与原问题相似的规模较小的问题——正序打印 123 的每一位。那么正序打印 123 ,又可以分成先正序打印 12 再加上单独打印 3 ,以此类推,又可以把问题分形成正序打印 1 再加上正序打印 2 ,当只剩一位的时候自然也就没法差分了,就直接打印 1 。

上代码:

#include<stdio.h>
void print(int num)
{//只有一位数直接打印if (num < 10){printf("%d ", num);}//不止一位数else{//num/10,就是去除num的最后一位,打印num最后一位之前的每一位。print(num / 10);//加上单独打印num的最后一位。printf("%d ", num % 10);}
}
int main()
{int num = 0;scanf("%d", &num);print(num);return 0;
}

运行结果:

递归函数展开图解

这里图解中红色路线叫做递推,蓝色部分回归,这就是递归的展开思路。

但是这里还是要提醒大家,大家理解递归代码的时候,千万不要陷入一种惯性思维,就是见到递归就想着去坐进递归,一般递归都带着严谨且复杂的逻辑,我们是无法彻底走到底的。关键在于要利用递归这种大事化小的思维去整体理解递归。

(2)编写函数不允许创建临时变量,求字符串的长度(利用递归求解)

思路点拨:

这里先简单介绍一下,怎样求一个字符串的长度,再来看怎么用递归求解。

首先,C语言提供了一个求字符串长度的函数——strlen(  );我们知道字符串的结尾都会隐含一个字符' \0 ' ;这个 ' \0 '就是字符串的结束标志,所 strlen( )也是通过查找 ' \0 '来确定字符串长度。聊到这里我们也就可以尝试使用递归解决了。

这里我们假如有一个字符串 char * p="abcdef";然后用另外一个指针表示我们的字符串,

当我们的pp指针指向的字符不是' \0 ' 时,就代表字符串至少有一个字符,加上pp向后移动一位,代表的字符串的长度就是,总字符串的长度。

所以代码就很简单了

#include<stdio.h>
int Strlen(const char* str)
{//str指向的字符是'\0';就代表已经到达字符串的结尾,返回 0 if (*str == '\0'){return 0;}//str指向的字符不是'\0'; 就代表长度至少是 1,跳过这个字,//再加上后面的字符数就是,总长度了。else{return 1 + Strlen(str + 1);}}
int main()
{char* p = "abcdef";int len = Strlen(p);printf("%d\n", len);return 0;
}

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

首先我们了解一下什么是斐波那契数,1 ,1 ,2 ,3,5,8,13,21,34,55 ........这样第一和第二个数是 1,从第三个数开始是,前面两个数的和。这就是斐波那契数列,我们称数列中的数是斐波那契数。这个规律显然易见,就可以写出一个表达式:

代码:

#include<stdio.h>
int fib(int n)
{if (n <= 2)return 1;elsereturn fib(n - 1) + fib(n - 2);
}
int main(){int n = 0;scanf("%d", &n);int num = fib(n);printf("第%d位斐波那契数是%d\n",n, num);return 0;
}

但是当我们算的位数比较大时就会计算非常慢,这就是因为,再用递归计算斐波那契数时,会产生大量重复的计算。

这里我们看一下,求第40位斐波那契数,第三位斐波那契数算了多少次:

#include<stdio.h>
int count = 0;//全局变量
int fib(int n)
{if (n == 3)count++;if (n <= 2)return 1;elsereturn fib(n - 1) + fib(n - 2);
}
int main(){int n = 0;scanf("%d", &n);int num = fib(n);printf("第%d位斐波那契数是%d\n",n, num);printf("第三位斐波那契数算了%d次\n", count);return 0;
}

这里第三位斐波那契数算了将近四千万次,所以才会效率这么低。

斐波那契数也是可以用循环来计算的:


#include<stdio.h>
int fib(int n)
{int result;int pre_result;int next_older_result;result = pre_result = 1;while (n > 2){n -= 1;next_older_result = pre_result;pre_result = result;result = pre_result + next_older_result;}return result;
}
int main(){int n = 0;scanf("%d", &n);int num = fib(n);printf("第%d位斐波那契数是%d\n",n, num);return 0;
}

提示:
1. 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。
2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。

四.经典递归案例

1.汉诺塔

法国数学家爱德华·卢卡斯曾编写过一个印度的古老传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。

当只有一层时:

只需要A挪到C就可以了,A—>C;需要 1 步;

当有两层时:

需要A—>B,A—>C,B—>C,需要 3 步

当有三层时:

A—>B ,A—>C, B—>C, A—>C ,A—>B ,A—>C, B—>C,需要 7 步。

这里就能看出规律:

层数 步数
1 1
2 2^2 -1=3
3 2^3 -1=7
4 2^4-1=15

步数=2的层数的次方减去1,当有n层时,步数:count = 2^n -1;

我们怎么用递归的思路去解决n层的汉诺塔呢?

n我们也可以看成两层,第一层时由n-1层构成的,第二层是第n层。要想完成n层汉诺塔,就必须要先把n-1层放到B上面,再将第n个从A挪到C;最后将n-1层汉诺塔从B挪到C;而把n-1层汉诺塔从A放到B上面,就又是一个n-1层汉诺塔问题了,还是相似的问题,但是规模变小了。所以先把上面n-1层看成一个整体,而n-1层的八个汉诺塔就是递归来完成的,这就是整体去用递归解决问题。

#include<stdio.h>
//汉诺塔问题
int Hanoi(int num,char n1,char n2)
{static int count = 0;//记录步数;if (num == 1){printf("%c->%c ", n1, n2);count++;}else{//将n-1层汉诺塔从A挪到B;Hanoi(num - 1,'A','B');//将第n个从A挪到C;printf("%c->%c ",'A','C');count++;//将n-1层汉诺塔从B挪到C;Hanoi(num - 1, 'B', 'C');}
}
int main()
{int num = 0;printf("请输入汉诺塔层数:>");scanf("%d", &num);printf("完成%d层的汉诺塔的方法是:\n", num);int sum=Hanoi(num,'A','C');printf("\n");printf("所以完成%d层的汉诺塔需要%d步。\n",num, sum);return 0;
}

2.青蛙跳台阶

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

一个台阶:有 1 种跳法,跳 一 下。

两个台阶:有 2 种跳法,1 1 或者 2(1代表一次跳一个台阶,2代表一次跳两个台阶,下同)

三个台阶:有 3 种跳法,1 1 1 或者 1 2 或者 2 1;

四个台阶:有 5 种跳法,1 1 1 1,1 2 1,2 1 1,1 1 2 ,2 2;

规律:

台阶数 方法种数
1 1
2 2
3 3
4 5
5 3+5=8
6 5+8=13
n n-1个台阶跳法 + n-2个台阶跳法

除了当只有一个台阶和两个台阶时,跳的方法是 1和 2;台阶数大于2以后的方法数都是前两个台阶数的方法数的和。n个台阶数的方法数:count = n-1个台阶跳法 + n-2个台阶跳法。

细心的小伙伴会发现和斐波那契数列很像;

接下来代码就很简单了:

#include<stdio.h>
int frog_jump(int num)
{//一层台阶if (num == 1){return 1;}//两层台阶else if (num == 2){return 2;}//大于两层台阶else{return frog_jump(num - 1) + frog_jump(num - 2);}
}
int main()
{int num = 0;printf("请输入台阶数:>");scanf("%d", &num);int count=frog_jump(num);printf("%d层台阶青蛙一共有%d种跳法。\n ",num, count);return 0;
}

最后:

虽然永远无法预料明天是晴还是雨,也无法预知你在乎的人是否还在身旁,以及你一直以来的坚持究竟能否换来什么。但你能决定的是,今天有没有备好雨伞,有没有好好爱自己,以及是否为自己追求的理想而拼尽全力。那些看似不起波澜的日复一日,会在某天让你看到坚持的意义。诸君,山顶见!!!

深度理解递归,手撕经典递归问题(汉诺塔,青蛙跳台阶),保姆级教学。相关推荐

  1. 【Java数据结构与算法】第十七章 二分查找(非递归)和分治算法(汉诺塔)

    第十七章 二分查找(非递归)和分治算法(汉诺塔) 文章目录 第十七章 二分查找(非递归)和分治算法(汉诺塔) 一.二分查找 1.思路 2.代码实现 二.分治算法(汉诺塔) 1.概述 2.汉诺塔 一.二 ...

  2. 经典算法之汉诺塔求解问题

    法国数学家爱德华·卢卡斯曾编写过一个印度的古老传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针.印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64 ...

  3. 递归经典例题:汉诺塔问题

    目录 问题描述 分析问题 解决问题 总结 问题描述 汉诺塔问题是一个经典的问题.汉诺塔(Hanoi Tower),又称河内塔,源于印度一个古老传说.大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上 ...

  4. 要理解递归,得先理解递归--用Java语言由浅入深讲解汉诺塔游戏

    2019独角兽企业重金招聘Python工程师标准>>> 一.递归是什么? 定义:程序调用自身的编程技巧称为递归.它分为调用阶段和回退阶段,递归的回退顺序是它调用顺序的逆序. 递归使用 ...

  5. 汉诺塔递归python搬运次数_Python3.x | 汉诺塔递归理解

    在刚学廖雪峰廖大佬的python3教程中的递归时,前面的内容理解都觉得还行,到了做汉诺塔的练习时会觉得有些发懵,后面多看几遍和练习后也就理解了. 因为遇到有人在问这个问题咋理解,因此写下我的想法,希望 ...

  6. 用类比方式学习编程中函数递归(个人理解仅供参考)(内含汉诺塔问题的求解)

    目录 1.前言 2.递归的数学模型 3.相关的c语法 4.将递归的数学模型写成编程语言 5.利用类比方法将实际问题的代码写成函数递归的形式 例1: 例2: 6.汉诺塔问题的求解 1.前言 本人在学习函 ...

  7. 【Python】函数递归实例之字符串反转、汉诺塔问题分析

    递归的定义 函数定义中调用函数自身的方式 两个特性: 链条:计算过程存在递归链条 例如,n!=n*(n-1)!,n!与(n-1)!就构成了递归链条 基例:基础的实例,存在一个或多个不需要再次递归的基例 ...

  8. 递归:这帮小兔子崽子、汉诺塔游戏+习题复习

    ##坑爹的兔子 ##斐波那契数列的迭代实现            ※我们都知道兔子繁殖能力是惊人的,如下图:            ※我们可以用数学函数来定义:            ※课间练习:假设 ...

  9. python汉诺塔递归并统计次数_Python之汉诺塔递归运算

    汉诺塔问题是一个经典的问题.汉诺塔(Hanoi Tower),又称河内塔,源于印度一个古老传说.大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘.大梵天命令婆 ...

最新文章

  1. dede问答模块 那个php文件相对重要,DEDE5.7 问答模板的修改技巧
  2. 数据结构 练习21-trie的原理分析和应用
  3. Oracle/MySQL/PostgreSQL考题等你挑战(附假期活动获奖名单)
  4. neo4j limit
  5. [python] ZZ 随机数生成
  6. Introducing and integrating Hibernate(Chapter 2 of Hibernate In Action)
  7. Windows8中pid为4的system进程占用80端口的解决办法
  8. P1809 过河问题_NOI导刊2011提高(01)
  9. VTD-XML解析XML入门简介
  10. 对称加密算法之Java SM4算法应用 附可用工具类
  11. MySQL表更新记录
  12. css3新单位vw、vh、vmin、vmax的使用详解
  13. ad元件定位孔放在哪一层_机械制造技术(1)——定位误差的计算
  14. 自己写的年会抽奖软件免费版带后门作弊,共享出来给大家(更新至V1.3)——转自哈尔滨健康生活网
  15. QQ空间g_tk算法的JS脚本的获取和分析
  16. 批量爬取网站图片-“优美库”篇(爬虫实战)
  17. 唐山盐碱滩成渤海明珠 国稻种芯·中国水稻节:河北曹妃甸大米
  18. 微信小程序自适应深色主题DarkMode源码
  19. 2021-C++程序设计-实验3-继承和虚函数
  20. ssh登录极路由后台_使用SSH来远程使用服务器上的可视化软件

热门文章

  1. ELK性能优化实战分析:如何化身BAT面试收割机
  2. linux 下反斜杠路径,Linux下反斜杠号\引发的思考
  3. c# 反斜杠 双斜杠_C#程序打印反斜杠(\)
  4. vue 实现指定日期之间的倒计时
  5. docker部署及简单使用
  6. PKE智能感应摩托车防盗器一键启动无匙进入怎么操作怎么用
  7. php生成/读取excel的PhpSpreadsheet库使用方法
  8. CGI 程 式 设 计
  9. Ubuntu 16.04 将 Noto CJK 作为默认中文字体
  10. 岳阳借“数字城管”打造“智慧城市”