00:00
大家好,我是海波老师,接下来我们来讲一讲Java中方法的一种特殊调用方式,我们叫递归,我这里呢,把这个程序呢,我复制一下,把它改成我们的16,把后面这个改成叫REC,然后呢来咱们点击OK,点击完成以后,把里面的这些东西啊,咱们全给它去掉,然后呢写上它啊,我们称之为叫递归。好,把这个去掉。行了,去掉完成以后呢,我们这里说一下什么叫做递归呢,咱们这里简单的说一下啊来,呃,所谓的递归啊,它其实呢,就是方法调用自身的一种编程方式,就是方法调用自身。所以呢,一个方法如果在执行逻辑中啊,不断的调用自己的话,那么这个方法咱们就称之为叫递归方法啊,我们称之为叫递归方法就是这样的,诶很奇怪,那为什么要自己调用自己呢。很简单,就是逻辑相同啊,比如我现在想求啊,20以内的奇数之和,我该怎么求呢?大家想想诶,我们来看看啊,我们这里呢,简单的写上一下,20以内的奇数之和,就是一加三加上五加上七,再加上我们的九点点点点点,再加上我们的19对不对?这不就是所谓的20以内的奇数的求和吗?那如果我们想求和的话,大家有没有发现这里的数据之间其实是有规律的。
01:21
从我们的第二个数开始,我们的每一个数和在前面这个数它都相差二,所以啊,这些数据我们组合在一起,就是我们数学当中的一个叫等差数列,对吗?那么求等差数列的方法有很多呀,我们这里呢,来实现其中的一种方法。那么大家想一想,咱们当前的问题是求20以内的奇数之和,其实啊,这个20呢,可以换成任意的正整数,那咱们就换小点吧,你20有点多了,对吧?其实我们只要找到规律就可以了,你20和十呀,或者30呀,没有本质的区别,对吗?大小都是一样的,我们这里啊,就以十为例吧,那么从逻辑上我们需要判断一下你给我的这个数它是不是奇数,比方说你有给你给我个19,那19本来就是奇数,对不对?我就可以直接做计算了,那如果你给我的是20呢,那20就不是奇数嘛,我还得把它减去那个值对不对?所以我们在传值的时候,我们是需要判断我当前的那个值是我们的奇数还是偶数,对吗?哎,就是这样好了,我们画个图,咱们把。
02:21
这个说明一下啊,来咱们首先呢,我们在这里呢,就画上我们写上一个我们的一对吧,我们说了咱们在十以内吧,咱们就是13579吧,所以我这里给它一个颜色啊,咱们来换一个颜色,然后写上一个一,好了,写完了以后,我复制一下我们的一,我写个三啊一三,然后呢,我们在这里来,我们写上它,我们写上这个我们叫做五。把这个呢,我们拷贝一下咱们的七,哎,放过来,还有呢,我们这里呢,再来一个啊,我们写上一个我们的九可以了,那我写完了以后,同学们13579,现在我就想求他们的和呀,所以呢,我这里给它加上一个范围,把它包起来,我们想求的就是它的和,诶放过来好了,放过来以后颜色呢,给它标上一个灰色吧,诶我们就想做这样的一件事情。
03:05
那如果我们想做这样的一件事情的话,大家想想我们该如何去做计算呢?但是其实啊,有个问题,因为啊,我们给大家说的是我们的十以内,或者说20以内,其实我们是从十和20的角度来考虑这个等差数列的,但是我们当前的这个数据呢,它是从一开始的,它到结尾,这样的话,跟咱们的思考方式啊,稍微的有点不同,所以呢,我调整一下干嘛呢?我把这个等差数列呀,咱们调整一下,咱们从九开始,所以把九我们拿到这儿来,把七我们拿过来,咱们是九,七什么呢?我们的五诶,我们的三诶,我们的一。我把它调一下,咱们调整一下,好翻过来,把一我们拿过来。所以啊,咱们的思路呢,按照咱们刚才给一定的问题的思路,咱们讨论一下。行了,那我现在呢,我们就是97531,求他们的和本质上跟13579没有区别,对不对?好诶,那么我们现在就想个问题啊,我现在它是九的什么等差数列没问题吧。
04:06
但是我们能不能这么理解呢?把这个咱们复制一下,复制以后干嘛呢?你把当前的这个范围呢,给我缩小一下,诶我这么写,你这么写了以后不就等于九来加上七的等差数列吗?同学们,这个大家能不能明白,所以也就意味着我这里加个小图标啊来。咱们这里写上它,诶咱们叫做加号,把这个加我放过来,加的时候呢,我就放到这个就可以了,颜色呢,我给它变成我们当前的什么,我们的一个绿色,诶我们的这个数据不就等于九加上我七的等差数列吗?没问题吧,7531不就是七的那个等差数列吗?好,如果这个大家能明白的话,那就简单了,为什么?因为我们上面的这个计算可以分解成我们的九加上七的等差数列,那我的七的等差数列也可以把它优化一下呀,咱们复制。复制以后大家看一下来,我把这个它再缩小一下我们的范围,然后呢,把这个七我独立出来放过来,放过来以后大家想想是不是就意味着我们七的等差数列就等同于七,再加上什么五的等差数列,这个大家能不能明白?
05:14
如果能明白的话,咱们思路呢,就是完全一样的了,我们五的等差数列来咱们复制一下,复制复制以后放到这边来,把这个五呢,我放到这边对吧,然后呢,把它给个绿色同学们放过来了,然后呢,把这个加号我放到这里没问题吧,再把这个箭头我们给它放到这里,你看。我们现在等于五,再加上我们三和一的是吧,等差数列的值,OK,那现在我再来,那我三的等差数列不就等于我们的三再加上一嘛,一就没有所谓的等差数列了吧?所以我们的三,诶,放过来好了,你放过来以后把这个拿过来,同学们现在能不能明白老师想说明什么问题,有没有发现我们的计算是有规律的,什么规律呀,我可以一乘一乘把它简化呀,我们所谓的九的等差数列就等于九再加上它的七的等差数列,对不对?我们七的等差数列等于我们的五的等差数列,你看以此类推啊,但是你要记住了,到了咱们的一就没有等差数列了,为啥呢?因为我们这个东西要是负数的话就没有意义了嘛,所以我们的这个一就到头了,到头的话它就没了。
06:22
他没的情况下,他就应该有一个结果了吧,对不对,就是这个道理,所以咱们分析到这一步的话,基本上就是OK的好了,那我们图画的没有问题,但是需要注意啊,我们到了这个一的时候,我们就应该干嘛算完了吧,说明我们的计算是有边界的,你不能无限的去算,你求和,你这个一再去什么找个负数,那你想想你求和不就少了吗?那不对,所以到了一的时候,咱们就不再往下去做计算了,咱们应该有一个计算的边界,对吗?好了,同学们分析完了,规律咱们也找到了,那是不是开始要计算了呢?那怎么算呢?哎,记住分析的过程是我们从前往后,那么计算的过程就是从后往前了,什么意思啊,同学们来把这个箭头呢,咱们复制过来,同学们,诶不对,我这个地方我应该把它呢复制一下来拷贝。
07:14
拷贝以后同学们把这个过程呢,我们往这放。咱们往这边方啊,放过来,放过来以后把这个过程正好相反,什么意思呢?我的一我算出来了呀,为什么呢?它没有等差数列,它就是我们的值呀,所以我给他个红色,我开始往回返了,往回返的话,同学们一的这个值那不就是一吗?所以我的一就可以把它替换掉,同学们没问题吧,那没问题的话,我三加一,诶这个箭头往回返,记住往回返,往回返的话,三加一不就等于四了吗?所以这个值应该等于几啊,等于四这个一就没了,对吧?咱们现在要计算了嘛,所以四应该是一个红色,没问题吧,好。如果没问题的话,五加四不就应该等于九了吗?所以这个值诶往回返,那么它就应该等于九了,所以把这个去掉,把它也去掉,也去掉对吧?那么我们七加九,那么不应该是我们的16了吗?没问题吧,同学们,所以这个地方应该是我的16对吧?所以把这个呢,我们去掉去掉,去掉我们的16呢,应该标上我们的红色,没问题吧,然后把这个箭头干嘛呢?往回返。
08:22
往回返之后,16加九是多少?25啊,所以这个值不就应该是我们的25了吗?把它们都去掉,同学们有没有发现我最后的结果就求出来了,怎么求出来的?就是按照我们的规律求出来的,我先一步一步把每一步的操作给我分解开,分解开以后我再从我的最后往前一个一个往回推,那么最后的结果就是25。没错吧,咱们分析一下来,咱们找一下我们的一加上三加上五加上七加上九,那可不就是25嘛,对吧,十以内的奇数之和,它就是25啊好了同学们,我刚才是通过画图的方式给大家把整个的流程我分析了一下,那么接下来我们准备通过代码的方式给大家来实现一下,那我该怎么实现呢?诶,我们这里写上一个方法,我把这个方法呀,咱们原封不动,我拷贝一下。
09:15
拷贝完成以后干嘛呢?改个名称,我们的目的非常简单,就是想求等差数列,咱们写上叫compute。好了,我们叫AP啊,等差数列,把这个呢,我们就去掉,我现在想干嘛呢?我就想告诉你我是谁以内的,比方说我写个number。好了,那我在这儿呢,就拷贝一下,拷贝以后我在这儿干嘛呢?来写上它,我们叫做20,我就想求20以内的什么等差数列的和呀,那所以这个方法呢,就应该返回一个值呀,对吧,你得有结果呀,所以我们的结果写上它叫out,好了,有了结果之后,我这里直接去打印咱们的结果就完事了,20以内咱也别求20了,刚才不算过十以内的吧,只要十以内的结果没问题,基本就差不多了。
10:03
好了同学们,那接下来咱们就说一下,那么这时候有个问题啊,什么呢?你的这个十呀,它不是一个基数啊,你要从一个最近的基数,比它小的最近的基数来开始算吧,所以我这里改一下,把你这个数啊,我做个判断,如果你模拟二,什么叫模拟二,看你的余数,如果你的余数它等于零,说明你是一个什么,我们的偶数啊,如果是偶数的话,你不能作为起点,所以我们需要把这个number我减个一。同学们,这个能不能明白,如果你是个偶数的话,我要从比它小一的那个值开始算,从九开始算,没错吧?那如果你本身就是个奇数的话,那么我就拿你来算就行了,哎,给他个number,我这么写就可以了,这样的话把咱们当前的值我们判断了一下,那么判断了以后呢,就简单了呀,同学们,为什么呢?你看我们这个图,这个图形当中,大家想想我们九的等差数列不就等于九,再加上它减这个等差数列的这个七的这个什么我们的和吗?
11:06
所以说就等同于什么呢?我们来写上,它叫return,我们叫做什么呢?Number,记住我给的是个十,但是它会算出来九吧,那么我的九它再加上什么呢?我的number再减去二,因为我们是奇数嘛,等差数列差值为二嘛,所以二就是它的差值,但是你要记住,我求的是九和七的等差数列的值。这个不就在计算等差数列吗?所以拷贝拷贝以后复制过来,你这么写就行了。就这么简单呀,同学们,你当前是九,那么好,九减27,七的等差数列的值不就可以了吗?同样道理,你要想算七的等差数列,那么它是七,那它不就是七加是五的等差数列吗?那么如果是五的话,五再加上五减二就是三的等差数列,三再加上一的等塔数列对不对?不就这么回事吗?但是同学们,关键点来了,还记得我们前面讲过,咱们图形当中,咱们到了一就不能再往下了,为什么?因为它是一个边界。
12:11
对吧,咱们的计算是有边界的,你不能无限往下减呀,那就不对了,所以呢,我们这里的判断一下叫做number。你当前的这个number啊,你如果是一的话,我们直接返回了,就别再往下算了,对吧,如果你是一,咱们就直接RETURN1,为什么呢?不用再做等差操作了,我们直接往回返,但是如果你要大于一的话,咱们else,对吧,那你就可以做这个操作吗?所以你把这个放过来,同学们这个就可以了。这个就算完了,记住啊,咱们当前这个方法算的就是我们的等差数列呀,就是这么回事儿,哎呀老师这个对不对啊,咱们来验证一下,怎么验证呢?我说了十以内13579,它的结果就应该是25,咱们运行一下,看结果运行。
13:00
运行以后没有任何的问题吧,同学们你看结果是对的,所以啊,我们在方法当中,我调用了自己的这种方式,我们就称之为叫递归方法,你看这就叫递归,就这么简单呀。哎呀,老师,那还有没有别的功能呢,对吧?好,我们再给大家来一个啊,这里呢,我们给大家实现另外一个功能,就不叫等差数列了,咱们来咱们求另外一个功能,咱们叫什么呢?叫阶乘啊。这个阶层的概念,不知道大家有没有这个听说过啊,阶层呢,其实是一个数学的运算符号,它就是什么呢?在我们的这个数字的后面,比方说五啊,我写个感叹号,这个就表示的叫做什么呢?叫做阶层。那好,什么是阶乘?怎么理解这个感叹号的运算符呢?这个跟我们那个之前讲的Java中那个感叹还不一样,我说的是数学运算中的那个符号,它那个感叹号叫阶乘,不是说咱们Java当中那个运算符,这是两回事,不一样啊,咱们Java当中那个是逻辑关系,咱们这个感叹号不是用来做运算的啊,这个咱们说一下。
14:03
那么这个五的阶乘是什么意思呢?他说了,所谓的阶层呢,其实就是一个正整数的阶乘,它是所有小于等于这个数的正整数的积。成吉呀,嗯,不明白啊,你这个乘呢,就是成吉的意思,可是怎么理解?我们的五的阶乘等于。该数和它小于它的所有的正整数的积呢,那咱们就想想吧,所有小于五的正整数的话,不就是4321吗?他就跟你说了,我们五的阶乘等于五,就是这个数啊,和它们相乘,哎,所以呢,它的结果是什么呢?是五乘四乘三乘二乘一,哎,就是这样的,还有一个关键点,零的乘为一啊,这个是一个比较特殊的一个点了,这是我们的零啊,它的阶乘为一啊,这个咱们要说一下。
15:00
哎呀,那好了,那这个倒是也不难吧,可是怎么实现呢?我相信大家可能没什么思路,对吧,等差数列咱们分析以后你没问题,但是呢,我们这个阶层吧,感觉好像也有规律,那你说我该怎么做呢,对不对?诶咱们是不是再画图再理解一下呢?其实告诉大家不用,为什么呢?因为啊,我在刚接触阶层的时候啊,别人是这么告诉我的,就是一个大于一的数的阶层。它等于这个数啊,乘以我们这个数啊,减一的阶乘这个数咱们叫减一的阶乘,其实呀,我还是不明白什么是阶层,但奇怪的是呢,我居然能按照这个方式把代码写出来,诶大家看一下,我这么来写,我首先呢,我来写上一个方法来拷贝一下,把这个方法我拿过来,拿过来以后我就改个名称啊,咱们说一下咱们要计算什么呢?我们的这个阶层了,诶我就这么写。所以啊,那阶层的话,我写个number吧,五的阶层我就传个五就行了,所以我就写上它,咱们就到来拷贝,拷贝以后我放过来写上一个五,然后呢,点一下我们给它一个结果,五的阶层的结果来,我们写上它叫result,然后呢,在这里面呢,我们给它来啊,我们这里打印,打印我的result就行了,那好了,同学们,我刚才说过了,怎么理解这个逻辑呢?一个大于一的数的阶成啥意思,你的number大于一啊。
16:25
所以如果我们的if,你的number小于它等于一干嘛呀,我们就直接RETURN1就完事了,对吧,然后再写个else。L是什么呢?就是说我们当前我们的这个大于一,那大于一的话,它等于什么呢?等于当前这个数,它乘以我们的。它减一的阶乘。它的阶层不就在这儿吗?所以我们放过来,你把它放到这里就可以了,同学们,就这么简单,你看我这个就写完了呀,同学们,我当天就写完了,就那么简单,只要你把这个概念你描述清楚,我相信代码并不难写,对不对呀,算一算吧。
17:07
五乘四是我们的22,十乘三是我的60 60乘二不就一百二吗?对不对?一百二乘一不还是一百二吗?所以我们运行一下,看结果,运行之后没有任何问题,同学们,你看这不就是这样吗?它的本质上不还是自己调自己吗?也就是说我们逻辑之间是有关系的,对吗?就是这个意思。好了,同学们,所以最后我们还是总结一下方法,自己调自己,它就是递归,但是递归方法有两个需要注意的地方,就是我们的递归它应该有跳出的逻辑,咱们说一下。我们递归方法,它应该有跳出的逻辑。什么意思啊,如果你不跳出的话,你不断的在递归,不断在递归,你要记住啊,同学们,咱们讲过一件事情,咱们的方法执行会干嘛呀,在内存中压站呢,对不对?你看看咱们之前就讲过这个事情吧,咱们在方法中会压占的这个占空间就那么大点,你不断往里面压,不断往里面压,你跳不出去,那这个空间可能就不够用了,所以它就可能会出现问题啊同学们,来咱们演示演示啊,来把这个操作呢,我别写成了,我写个加号。
18:20
为什么呢,成就太大了,我写个加号,然后呢,我把这个改一改干嘛呢,我写上什么呢,我们的5万。好了,你写完了以后,我运行一下看结果,咱们看看运行以后大家看一下出错了吧,对不对?什么意思?当你不断的去压站,不断的去压占我们当前的占空间,就会出现问题,所以它就出现了一个叫做stack overflow arrow,它就出现了这么一个错误,所以啊,这里给大家去说一下,如果你不断的压战没有跳出就会出错,诶这是我第一个要给大家讲的问题,第二个讲的问题呢,就是我们逻辑上,我们的方法在断的调用自身的时候,传递的参数要有规律。所以啊,咱们这里说一下。
19:01
我们的这个调用自身时啊,调用自身时我们传递的参数,它需要有规律。大家会发现,我们在之前我不管给大家讲阶层也好,还是讲那个等差数列也好,其实他们都是有规律的,有规律咱们用这种方式没有任何的问题,对吧,自己教自己,逻辑上有关系嘛,但是如果你说老师我没有规律,能不能的,那肯定不行嘛,咱们举个例子吧,同学们来看看我现在说了啊,来,我现在写上把这几个数啊,咱们用我们递归的方式,大家想想你们能不能算得出来,来,翻过来好了。就把这几个数用我们的递归方式,你能不能算出来,你发现算不出来,为什么?因为它没有规律啊,你没有规律的话,你是不是就只能两个,两个算了它和它相加得结果,再和它做结果,对不对,没有规律呀,所以同学们递归方法的时候,它的参数传递应该有规律啊。好了,同学们递归方法咱们就讲到这里。
我来说两句