第 2 章 算法分析
2.9 递归(1)
1. 递归
递归(英语:Recursion),又译为递回,在数学与计算机科学中,是指在函数的定义中使用函数自身的方法。
例题 2.9.1 阶乘: ,并规定 。用递归的方式表示:
【算法描述】
long factorial(int n){
if(n == 0){
return 1;
} else {
return n * factorial(n - 1);
}
}
在此算法描述中,即使用了递归。
但,使用递归实现阶乘,并不是最优的选择,因为上述所谓递归,可以用循环语句轻易实现。
long factorial(int n) {
long fact = 1;
if(n == 0) return 1;
for(int i = 1; i <= n; i++) {
fact *= i;
}
return fact;
}
如果程序中出现了条件语句,其时间复杂度的判断应以运行时间长的分支为准,例如在上述任何算法中,显然 if(n == 0)
分支的时间复杂度是 ,而另外一个分支,时间复杂度是 ,故该算法的时间复杂度是 。
例题 2.9.2 斐波那契数列
公元1150年印度数学家 Gopala 和金月在研究箱子包装对象长宽刚好为 1 和 2 的可行方法数目时,首先描述这个数列。在西方,最先研究这个数列的人是比萨的列奥那多(意大利人斐波那契 Leonardo Fibonacci, 1175-1250),他描述兔子生长的数目时用上了这数列:
- 第一个月初有一对刚诞生的兔子
- 第二个月之后(第三个月初)它们可以生育
- 每月每对可生育的兔子会诞生下一对新兔子
- 兔子永不死去
假设在 月有兔子总共 对, 月总共有 对。在 月必定总共有 对:因为在 月的时候,前一月( 月)的 对兔子可以存留至第 月(在当月属于新诞生的兔子尚不能生育)。而新生育出的兔子对数等于所有在 月就已存在的 对。
斐波纳契数是帕斯卡三角形的每一条红色对角线上数字的和。
用递归方式定义斐波那契数列:
【算法描述】
long fib(long n){
if(n == 1 || n == 2) return 1;
else return fib(n-1) + fib(n-2);
}
对斐波那契数列算法的时间复杂度分析,后面会单独介绍。
上面两个例子中显示,用递归实现算法,必须要确保有穷性,这也是算法的特性之一(参考 006 节),为此,必须有递归终止的条件,例如斐波那契数列的算法描述中的第 2 行。终止条件处的结果应该是直接算出来,而不依靠递归的。这是递归的基本法则之一。
递归的基本法则:
- 基准情形(base case):必须要有某些基准的情形,不用递归就能求解,也就是递归的终止条件。
- 不断推进(making progress):对于那些需要递归求解的情形,递归调用必须总能朝着产生基准情形的方向推进。
- 设计法则:假设所有的递归调用都能运行。
- 合成效益法则(compound interest rule):在求解一个问题的同一实例时,切勿在不同的递归调用中做重复性的工作
对于斐波那契数列的递归算法描述,就违背了第 4 点,后续专门进行时间复杂度分析的时候会详解。
本文由mdnice多平台发布