前言:
而今姐妹们对“java中的递归”大约比较注重,看官们都需要分析一些“java中的递归”的相关资讯。那么小编在网摘上汇集了一些关于“java中的递归””的相关文章,希望同学们能喜欢,兄弟们一起来了解一下吧!1.递归算法
递归在计算机科学中也称为递归算法。一些问题在分解的时候会有这样的现象:解决该问题的过程是由重复的相似子过程组成,类似这样的问题我们都可以通过递归算法进行解决。在计算机语言中,递归算法的实现靠函数的自我调用来完成,我们所见到的大多数高级编程语言都支持这样的做法。
递归算法通常有两种方式实现——普通递归和尾递归。递归方法简单的来说就是方法的内部再次调用自身,递归方法会嵌套的参与运算。这样每一个递归方法都要分配一个函数堆栈进行操作,这就是普通递归。普通递归对内存的消耗是非常大的。
另一种递归方式被称为尾递归,尾递归对普通递归进行了优化。如果使用尾递归,需要将递归方式进行特殊的设计,它需要将递归方法在return语句后进行单独调用(即尾调用)。当采用尾递归的时候,一些编程语言会进行优化,将所有嵌套的递归方法放在同一个函数堆栈中进行,效率非常快。作为一名Java程序员,如果你无法将递归方法设计成尾递归的模式也没有任何问题,因为Java并没有对尾递归进行优化 ,Java对内存的优化是依赖于回收机制。但是如果你是一名C程序员,就需要对尾递归的写法进行掌握了。
递归算法的优缺点是非常明显,算法实现简单、可读性强是递归算法的优点所在。缺点也同样明显,递归算法会占用大量内存空间,如果递归深度过大,容易发生内存相关问题。所以在递归算法中,有这样一句话:不用递归累死,滥用递归慢死。如何合理的使用递归算法,是递归使用的关键问题。
2.怎么使用递归
在设计递归算法的时候,一定要注意两点:1、设计出等价的递归公式。这一点需要我们拥有一些数学基础以及抽象概括能力,能够在复杂的运行过程中,抽象出等价的函数关系。
2、递归退出的条件。这一点尤为重要,如果递归方法没有结束条件,就如同死循环一样,让内存和CPU直接"撑爆"。常见的递归练习方法有斐波那契数列和汉诺塔移动算法。
2.1 斐波那契数列(Fibonacci sequence)
斐波那契数列是经典的递归算法应用,它是一组有规律数列:"1,1,2,3,5,8,13……",当我们要获取数列中第n位的数字时,可以总结如下公式:
当n=1或者2时,有f(n)=1,当>=3时,有f(n)=f(n-1)+f(n-2)
下面我们要设计一个方法,输出数列的前n位的信息,n通过整型参数控制。如果我们需要一个完整的数列,就需要创建一个数列容器,将数列中的每一位数字依次计算出来,并保存到容器中,最后按照顺序从容器中输出数列(如下列Java示例):
采用上面的做法好处非常明显,它能够记录每一位数列的值。当我们需要获取整个数列的时候,这样的方式是可取的。在一些时候,我们只想获取其中一位的数值,我们就不需要记录数列,这个时候使用递归的方式就非常方便(如下Java示例所示):
采用上述代码,可以直接获取到数列中第n位的数值。我们可以发现,使用递归的方式让代码更简洁、阅读起来更友好。下面我们创建两个测试方法,对上述两种方式进行测试:
运行结果:
2.2 汉诺塔(Hanoi)
汉诺塔是一种有趣的益智游戏,很多人在儿时都玩过这种类似的玩具(如下图所示):
汉诺塔的移动规则是将所有圆盘从A柱移动到C柱上,并保持上小、下大的有序顺序摆放。在移动的过程中,也需要保持这个规则。例如上图的三层汉诺塔,我们在移动的时候有如下步骤(如下图所示):
如果有多个盘子,我们设盘子总数为n,我们可以分为两部分解决,一部分是上面的n-1个盘子,它们作为一个整体,另一部分是最下面的盘子n。它们移动可以分为三步:
1.将第一部分的n-1个盘子的作为一个整体,从A移动到B柱上,C柱过度。
2.接着将第n个盘子从A柱移动到C柱上。
3.再将n-1个盘子的整体从B柱移动到C柱上,A柱过度(移动规律如下图所示)。
用代码实现的时候,我们就可以利用递归的方式进行移动。下面代码中,我们为了观察移动过程中,各柱子上盘子的变化情况,我们用队列来模拟柱子(实现代码如下所示):
运行结果:
3.递归对循环的替代
在程序开发过程中,很多循环方法都可以使用递归来完成,例如数字的累加和阶层的计算(如下面代码所示)。
运行结果:
在上述示例代码中,我们用递归和非递归两种方式解决了累加、阶乘的循环问题。除此之外,在一些数据结构算法中,递归的使用也非常多,比如二叉树的遍历、排序等。在下面的示例中,我们使用递归的方法进行冒泡排序。
示例运行效果:
传统的冒泡排序需要借助双层循环进行排序交换。如果使用递归的方式,可以减少一层循环。在实际的排序中,我们是不推荐使用递归进行排序的,上述示例仅作为递归算法的一种思考。
标签: #java中的递归