小伙伴们注意了!
小编在这里给大家送上关注福利:
搜索微信公众号“速学Java”关注即可领取小编精心准备的资料一份!
1. 递归
考虑阶乘函数:n!=n*(n-1)*(n-2)*…*1
计算阶乘有很多方法。一种方法是n!等于n*(n-1)!因此,程序可以直接写成:
项目1:
为了运行这个程序,计算机需要建立一个乘法链:阶乘(n)→阶乘(n-1)→阶乘(n-2)→…
→! (1)。
因此,计算机必须跟踪后面要执行的乘法。
这种程序的特征是一系列操作,称为递归。
递归可以进一步分为线性递归和树递归。
当跟踪操作链所需的信息量随输入线性增长时,这种递归称为线性递归。
n的计算!
是这样的情况,因为所需的时间随着n线性增长。另一种类型的递归,树递归,当信息量随着输入呈指数增长时发生。
但是我们将在这里不讨论它,稍后再讨论。
2. 迭代
一个不同的角度计算阶乘是首先把1乘以2,然后将结果乘以3,然后除以4,等等,直到n。更正式,程序可以使用一个计数器计数从1到n和计算产品同时,直到计数器超过n。因此,程序可以写成:
项目2:
与程序2相比,这个程序不构建乘法链。
每一步,电脑只需要跟踪产品的当前值和我。这种类型的程序叫做迭代,其状态可以总结为一个固定数量的变量,一个固定的规则,描述了变量应该如何更新,和结束测试指定条件的过程应该终止。
与递归一样,当所需时间随输入线性增长时,我们称之为迭代线性递归。
3. 递归与迭代
通过对这两个过程的比较,我们可以发现它们看起来几乎是一样的,尤其是在数学函数方面。
它们都需要一些与n成比例的步骤来计算n!
另一方面,当我们考虑这两个程序的运行过程时,它们的发展是非常不同的。
在迭代的情况下,程序变量提供了状态的完整描述。
如果我们中途停止计算,恢复计算只需要向计算机提供所有变量。
然而,在递归过程中,信息是由计算机来维护的,因此“隐藏”到程序中。
这使得在停止程序之后几乎不可能恢复程序。
4. 树递归
如上所述,当信息量随输入呈指数增长时,就会发生树递归。
例如,考虑定义如下的斐波那契数列序列:
根据斐波那契数列的定义,斐波那契数列的序列如下:0、1、1、2、3、5、8、13、21、……
递归程序可以立即写成:
项目3:
因此,为了计算fib(5),程序计算fib(4)和fib(3)。
对于计算机fib(4),它计算fib(3)和fib(2)。
注意,fib过程在最后一行调用自己两次。
从定义和程序中可以得到两个观察结果:
第i个斐波那契数列Fib(i)等于(i)/rootsquare(5)四舍五入到最近的整数,这表明斐波那契数列呈指数增长。
这不是计算斐波那契数列的好方法,因为它做了冗余计算。
计算这个过程的运行时间超出了本文的范围,但是在算法书籍中很容易发现,它是O(phi(n))。
因此,程序花费的时间随着输入呈指数增长。
另一方面,我们也可以用迭代的方式编写程序来计算斐波那契数列。
程序4是一个线性迭代。
程序3和程序4所需的时间差别是巨大的,即使是很小的输入。
项目4:
然而,我们不应该认为树递归程序是无用的。
当我们考虑在层次数据结构而不是数字上运行的程序时,树递归是一个自然而强大的工具。
它可以帮助我们理解和设计程序。
与程序3和程序4相比,我们可以很容易地看出程序3更直接,即使效率更低。
在此之后,我们很可能将程序重新规划为迭代的方式。
最后,想学习Java的小伙伴们!