从“数学归纳法”到理解“递归算法”!

归纳法适用于想解决一个问题转化为解决他的子问题,而他的子问题又变成子问题的子问题,而且我们发现这些问题其实都是一个模型,也就是说存在相同的逻辑归纳处理项。

接下来我们来看看,我们写程序和数学归纳法的关联。

2. 递归


说起递归算法,其实我们每个开发人员都肯定听过或者写过。记得我最开始接触递归算法的时候,还是大一学习谭浩强老师写的那本C语言时,里面介绍了递归算法。给我的印象就是:自己调用自己。后来在工作中,用到的地方也不多,印象中只有一次写级联菜单的时候用到了递归算法。(是不是我写的代码太水,大家也可以说说哪里用到过递归算法)本章就来通过数学归纳法来回顾下我们曾经学过的递归算法。

2.1 理解递归

递归的基本思想:以此类推

具体来讲就是把规模大的问题转化为规模小的相似的子问题来解决。在函数实现时,因为解决大问题的方法和解决小问题的方法往往是同一个方法,所以就产生了函数调用它自身的情况。另外这个解决问题的函数必须有明显的结束条件,这样就不会产生无限递归的情况了。仔细观察递归,就会发现:递归的数学模型其实就是归纳法

2.2 递归条件

我们在使用递归的时候需要满足一些基本条件,如果不满足的话,就有可能出现无限递归,最后会导致堆栈溢出了。

满足条件:

  1. 严格定义递归函数作用,包括参数,返回值,其他变量。

  2. 先一般情况,后特殊情况。

  3. 有退出条件。在一般情况下,能让递归正常退出的条件。

  4. 每次调用必须缩小问题规模,且新问题与原问题有着相同的形式,即规律。

上面的条件一环扣一环,也可以缩减成两个主要条件:有规律,有退出条件。我们以上面的条件,来结合案例进行理解。

2.3 小栗子

2.3.1 递归求和

例题:

1+2+3+…+n=?

第一步: 严格定义递归函数作用,包括参数,返回值,其他变量。

我们初看题目,可以知道这是一个简单的求和,即从1开始:1+2+3+…一直加到n。所以我们可以定义一个入参为n,返回值类型为int的一个方法,既然是递归求和,我们的方法名就叫recursionSum。

public static int recursionSum(int n) { //为了方便调用,我用了static

return 0;

}

System.out.println(“公众号:Coder编程:” + recursionSum(0));

那么我们第一步就做完了。

第二步: 先一般情况,后特殊情况。

我们先用一般的情况进行求和计算,例如代入1,2,3这样的一般情况。即:

public static int recursionSum(int n) {

if(n == 1) {

return 1;

}

if(n == 2) {

return 1+2;

}

if(n == 3) {

return 1+2+3;

}

return 0;

}

System.out.println(“公众号:Coder编程:” + recursionSum(3));

第三步: 有退出条件。在一般情况下,能让递归正常退出的条件。

其实,我们做完第二步,就会发现已经把第三步做完了。即有了让递归正常退出的条件!

第四步: 每次调用必须缩小问题规模,且新问题与原问题有着相同的形式,即规律。

这一步是最关键的,也是最核心的!我们需要找到其规律,并且能缩小问题的规模。我们会发现,当我们需要求第N个数的和的时候,我们必须知道前N-1个数的和,即 sum(N-1)。前N个数的和就是sum(N-1)+N。找到这个规律后,我们就可以定义一个临时变量sum来接收前N个数的和了。

public static int recursionSum(int n) {

if(n == 1) {

return 1;

}

if(n == 2) {

return 1+2;

}

if(n == 3) {

return 1+2+3;

}

int sum = recursionSum(n-1)+n;

return sum;

}

System.out.println(“公众号:Coder编程:前5个数的和” + recursionSum(5));

输出结果:15

我们优化一下:

public static int recursionSum(int n) {

if (n < 0){

throw new Exception(“参数不能为负!”);

}

if(n == 1) {

return 1;

}

return recursionSum(n-1)+n;

}

System.out.println(“公众号:Coder编程:前5个数的和” + recursionSum(5));

是不是突然发现递归其实也没想的那么难?

2.3.2 举一反三?

接下来我们难度进行升级!看大家能不能都理解了。我就不像上面求和那么啰嗦了!

2.3.2.1 求阶乘

例题:求n的阶乘(n>1,n是正整数)

阶乘的递推公式为:factorial(n)=n*factorial(n-1),其中n为非负整数,且0!=1,1!=1

这里就不做过多说明,跟求后过程一致,可以模仿求和的过程,大家可以先自己尝试写下,下面我直接贴代码了:

public static int factorial(int n) throws Exception {

if (n < 0){

throw new Exception(“参数不能为负!”);

}else if (n == 1 || n == 0) {

return 1;

}else {

return n * factorial(n - 1);

}

}

System.out.println(“公众号:Coder编程:3的阶乘:” + factorial(3));

输出结果: 公众号:Coder编程:3的阶乘:6

2.3.2.2 斐波那契数列

斐波那契数列 我想大家同样熟悉了解,下面我们继续回顾一下斐波那契数列到底是什么?

斐波那契数列图

斐波那契数列: 1、1、2、3、5、8、13、21…

可以看出从第三位起:第三项等于前两项之和。总结递推公式::Fib(n)=Fib(n-1)+Fib(n-2)。所以我们可以将前两位作为退出递归的条件。即:if(n==1) retrun 1 if(n==2) return 1

因此我们可以直接用公式(规律)和退出条件,写出编程代码:

public static int fib(int n) throws Exception {

if (n < 0) {

throw new Exception(“参数不能为负!”);

}else if (n == 0 || n == 1){

return n;

}else {

return fib(n - 1) + fib(n - 2);

}

}

System.out.println(“公众号:Coder编程:斐波那契数列:” + fib(3));

2.3.2.3 汉诺塔问题

相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置不同个数的金盘(如下图)。

游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。

操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。

汉诺塔图

在总结规律和写代码之前,我们先来玩几把简单的(先一般后特殊):

注:我们以数字的大小作为盘子的大小。

  1. 一个盘子的情况:

1.1 将A柱子的1号盘子直接移动到C柱子中。

1.2 结束。

  1. 两个盘子的情况:

2.1 将A柱子的1号盘子移动到B柱子。

2.2 将A柱子的2号盘子移动到C柱子。

2.3 将B柱子的1号盘子移动到C柱子。

2.4 结束。

  1. 三个盘子的情况:

3.1 将A柱子的1号盘子移动到C柱子。

3.2 将A柱子的2号盘子移动到B柱子。

3.3 将C柱子的1号盘子移动到B柱子。

3.4 将A柱子的3号盘子移动到C柱子。

3.5 将B柱子的1号盘子移动到A柱子。

3.6 将B柱子的2号盘子移动到C柱子。

3.7 将A柱子的1号盘子移动到C柱子。

3.8 结束。


我们会发现,随着盘子数量的增加,盘子移动的难度也开始加大。

这时候不要害怕,我们回过头再来看这个问题:当盘子的数量是4个、5个…N个的时候,我们该如何解决呢?我们是不是可以用数学归纳法的思想或者递归的思想去解决呢?答案是:肯定的。这时候我们需要去找到他们的规律在哪?

我们再观察下上面在一般情况下移动盘子的规律在哪?

  • 1.当只有一个盘子的时候,可以将盘子直接移动到目标柱子C中。即退出条件

  • 2.当只有两个盘子的时候,我们只需要将B柱子作为中介,将盘子1先放到中介柱子B上,然后将盘子2放到目标柱子C上,最后将中介柱子B上的盘子放到目标柱子C上即可。

第二点可以看成:当我们有N个盘子的时候,第N个盘子看成一个盘子,(N-1)个盘子看做成一个盘子。需要将(N-1)个盘子放在中介柱子B上,N个盘子放在目标柱子C即可。即规律

当我们有三个盘子的时候,我们会发现一个问题: 角色变化

  1. 将A塔座的第(N-1)~1个盘子看成是一个盘子,放到中柱子B上,然后将第N个盘子放到目标柱子C上。这时候柱子A空了!柱子A成为中介柱子,柱子B成为起始柱子

  2. 柱子B这时候有N-1个盘子,将第(N-2)~1个盘子看成是一个盘子,放到中介柱子A上,然后将柱子B的第(N-1)号盘子放到目标柱子C上。这时候柱子B空了!柱子B又成为了中介柱子,A成为了起始柱子!

重复1、2步骤,直到所有盘子都放到目标塔座C上结束。

总结一下:

  1. 从初始柱子A上移动包含n-1个盘子到中介柱子B上。

  2. 将初始柱子A上剩余的一个盘子(最大的一个盘子)放到目标柱子C上。

  3. 将中介柱子B上n-1个盘子移动到目标柱子C上。

move(3,“A”,“B”,“C”);

/**

  • 汉诺塔问题

  • @param dish 盘子个数(也表示名称)

  • @param from 初始柱子

  • @param temp 中介柱子

  • @param to 目标柱子

*/

public static void move(int dish,String from,String temp,String to){

if(dish == 1){

System.out.println(“将盘子”+dish+“从柱子”+from+“移动到目标柱子”+to);

}else{

move(dish-1,from,to,temp);//A为初始柱子,B为目标柱子,C为中介柱子

System.out.println(“将盘子”+dish+“从柱子”+from+“移动到目标柱子”+to);

move(dish-1,temp,from,to);//B为初始柱子,C为目标柱子,A为中介柱子

}

}

  • move(dish-1,from,to,temp);//A为初始柱子,B为目标柱子,C为中介柱子

这里需要将n-1之前的盘子都放到B柱子上,最后第n个盘子放到C柱子。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

Java面试核心知识点笔记

其中囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。

蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6

Java中高级面试高频考点整理

蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6

蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6

最后分享Java进阶学习及面试必备的视频教学

蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
、源码讲义、实战项目、讲解视频,并且会持续更新!**

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

Java面试核心知识点笔记

其中囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。

[外链图片转存中…(img-SXFZhAjE-1713473710038)]

Java中高级面试高频考点整理

[外链图片转存中…(img-Pw7gx02I-1713473710039)]

[外链图片转存中…(img-iq0wJesH-1713473710039)]

最后分享Java进阶学习及面试必备的视频教学

[外链图片转存中…(img-Pym4n206-1713473710039)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值