浅析递归算法

递归定义:重复将问题分解为同类的子问题而解决问题的方法,其核心思想是分治策略。

递归算法简单来说就是自己调用自己。最简单的如下图,但是它不正确,因为没有结束条件。

最简单的错误递归算法

这不禁让我想起了linux上一个经典的fork炸弹,代码是 :() {:|:&};:    非常经典,只有十三个字符,但是如果你在linux系统上执行他,要不了多久,你的系统就会崩溃宕机了。如果上面这个看的不直观,那么我们把方法命名一下(将原来方法名冒号【 : 】改成bomb 在来看下fork炸弹,如下图:

fork炸弹

这下是不是能看懂一些了? 函数的名称为bomb,主要的核心代码是 bomb|bomb&  可以看出这是一个函数本身的递归调用,通过 & 实现在后台开启新进程运行,通过管道实现进程呈几何形式增长,最后再通过bomb来调用函数引爆炸弹。因此,几秒钟系统就会因为处理不过来太多的进程而死机,解决的唯一办法就是重启。这就是递归的巨大威力,也能看出没有结束的递归方法的破坏力是多么的巨大!

看完fork炸弹的威力,我们回归到递归算法问题上来,相信通过上面的例子,相信大家对递归已经有个自己的了解,递归嘛,简单来说就是自己调用自己,但千万要有结束条件,没有结束条件的递归在写程序时绝对不被允许。相信我们学数学都学过 阶乘的概念,简单的说N的阶乘就是从 1*2*3*...*N  ,就是从1一直乘到N,那么我们怎么写程序求N的阶乘值是多少呢?对程序来说我们可以循环来求值,我们来个for循环,从1到N相乘,代码如下:

非递归求阶乘

是不是代码稍微繁琐点呢?那么我们用递归来求又是怎样的呢?看下面代码:

递归求阶乘

是不是非常简洁!代码的意思就是 要求的阶乘N的数值小于1就直接返回1,否则就返回 N乘 N-1的阶乘,至于N-1的阶乘是多少,那就变成了 求factrial(n-1) 的值了,依次类推,N一直在减少1,总归会小于1的,这时候对程序而言,调用的是factrial(0),它不用在递归了,直接返回1,那么这时候factrial(1)值也出来了,就是1*factrial(0) 也就是1,那么这时候factrial(2)的值也就出来了,就是2*factrial(1) 就是2  ......依次归并 直到N*factrial(N-1),所有递归也结束了,结果也就出来了!懂了吗!

如果懂了简单的递归方法,接下来我们来看一下稍微复杂的递归算法,也是一个经典的问题:汉诺塔问题。

问题背景:汉诺塔(Tower of Hanoi)源于印度传说中,大梵天创造世界时造了三根金钢石柱子,其中一根柱子自底向上叠着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。看下图:

汉诺塔问题

我们假设只有一个盘子,那么是不是直接从a到c就结束了!是不是和上面的阶乘类似?假如N为1那么结果就是A到C对吧?

那么两个盘子呢?其实只需要将上面的小的盘子放到B柱子上,然后下面的大的盘子放到C柱子上,最后将小的盘子从B柱子搬到C柱子上,然后就搬完成了!

2个盘子情况

这个A柱子就是起始柱子,B柱子,其实就是临时柱子(留过渡使用),C柱子就是结果柱子。如果盘数超过2个,将第三个以下的盘子遮起来,就很简单了,每次处理两个盘子,也就是:起始1 -> 临时、起始2 ->结果、 临时(上面是起始1的盘子)->结果   这三个步骤,而被遮住的部份,其实就是进入递回处理。

如果这个看不清楚,我们以三个盘子为例子:我们将上面两个盘子当成是一个,把他放到临时的 TOWER  2 柱子上,如下图:

1

然后我们将最大的移到TOWER 3柱子上,如下图:这时候问题是不是又变成了2个盘子移动的操作啦?只不过这时候,临时柱子成了TOWER 1 柱子了。我们只需要借助1号柱子将2号柱子的2个盘子移动到3号柱子即可。

2

问题到此是不是解决了!值得注意的是,相比阶乘,这边调用了自身两次,第一次递归是将N-1个盘子移动到临时柱子上,第二次调用是将临时柱子上的N-1个盘子移动到结果柱子上!想通了这一点,汉诺塔问题也就解决了!

汉诺塔解法算法

main函数如下:

java的main函数

最终结果如下:

结果

如果还不理解,推荐玩一玩汉诺塔小游戏:http://www.4399.com/flash/31851_2.htm  想必玩到5个盘子通过了,就能理解了!

咱们来总结一下递归:

1、递归就是 函数本身调用自己。

2、递归需要有结束条件!也就是说递归算法,在分解问题到不能再分解的步骤时,要让递归有退出的条件

到此递归的算法就结束了,如果您喜欢就点个赞吧!

你可能感兴趣的:(浅析递归算法)