什么是递归
(摘自百度百科):程序调用自身的编程技巧称为递归。递归是一种程序设计语言在中广泛应用的算法。 一个函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
(总结):函数体中出现调用其自身的语句,称为递归。递归分为直接递归与间接递归两种。
例题1 递归求解5!
思路启发
5的阶乘就是从1不断乘到5。
5!=5*4*3*2*1,不难发现,5!的结果就是4!与5的乘积,4!的结果就是3!与4的乘积,3!的结果就是2!与3的乘积......1!的结果就是0!与1的乘积,而0!等于1。
因此可以定义两个变量dep,sum表示递归层数和阶乘结果。每循环一层,sum乘上一次dep,然后dep加1。sum和dep的初值设为1。
设置终止条件
递归需要设置终止条件。而由于sum表示的是dep的阶乘,因此递归求5!时的递归层数(dep)不可以超过5。
因此我们可以在dep大于5(fu符合终止条件)时切出程序,直接输出sum,也就是5的阶乘。这样边界条件只需要一行代码:
if(d>n)
而这样的边界条件也是大部分递归程序的终止条件。
具体实现
时间复杂度O(N)。
具体实现代码如下:
void dg(int dep,int sum)
{
if(dep>5) printf("%d",sum);
else dg(dep+1,sum*dep);
}
那具体该怎么调用呢?由于dep和sum的初值在这个程序中都设为1,所以可以这样调用此递归程序:
dg(1,1);
题目扩展
1.求N的阶乘
如果要求的不是5的阶乘,而是一个数N的阶乘(N<=10)呢?
首先,不难发现,10的阶乘并没有爆int(3628800),因此这里是可以放心用int的。
然后只需要再定义一个变量N,然后将判边界条件时的5改成N就行了。最后在main函数里添加一行scanf就可以了。
具体实现如下(这里就不用万能头文件了):
#include<cstdio>//C语言改#include<stdio.h>
using namespace std;
int n=10;
void dg(int dep,int sum)
{
if(dep>n) printf("%d\n",sum);
else dg(dep+1,sum*dep);
}
int main()
{
scanf("%d",&n);
dg(1,1);
return 0;
}
试试样例(2,5,10):
那难道不能改单循环,甚至直接打表吗?当然可以,但是这样做并没有学会递归。
2.输出从1到N的阶乘
如果是从1的阶乘输出到N的阶乘呢?
直接在输出语句前面加一句return。我们可以每递归一次输出一次,直到符合终止条件为止(注意要把输出语句放在前面)。
这样的话程序如下:
#include<cstdio>//C语言改#include<stdio.h>
using namespace std;
int n;
void dg(int dep,int sum)
{
printf("%d",sum);
if(dep>n) return;
else dg(dep+1,sum*dep);
}
int main()
{
scanf("%d",&n);
dg(1,1);
return 0;
}
例题2 递归求解斐波那契数列的第N项
注:其中N<=50
思路启发
首先,在N=50的情况下,int是不够的,因此这道题要开long long。
斐波那契数列的第1项是1,第二项是2,第N项(N>2)的值是第N-1和第N-2项的总和。那么计算递归f(n-1)和f(n-2)的和就行了。
定义变量dep表示这是斐波那契数列的第dep项。然后根据斐波那契数列的规律倒推就行了。
设置终止条件
这道题目的终止条件与第一道不同。由于斐波那契数列的第1和第2项都是1,因此如果dep等于1时,返回1;等于2时,返回2就行了。
代码实现
正常代码实现如下:
#define ll long long
ll fib(ll dep)
{
if(dep==1||dep==2) return dep;
else return fib(dep-1)+fib(dep-2);
}
但这样写实在是太慢了,时间严重超限。那该怎么优化程序呢?
一个简单有效的优化
我们可以开一个F数组储存斐波那契数列中每一项的值。刚开始F数组全部设零。如果F[dep]被搜索过了(值不是0),那么就可以直接返回F[dep]。否则在递归求解斐波那契数列第dep项的同时将F[dep]赋为得到的结果。
这样可以避免大量的重复搜索,时间复杂度O(n),与循环时间复杂度相等。
这便是搜索优化中的记忆化搜索。
递归实现代码如下:
#define ll long long
ll n,f[51];
ll fib(ll dep)
{
if(dep==1||dep==2) return dep;
else if(f[dep]!=0) return f[dep];
else return f[dep]=fib(dep-1)+fib(dep-2);
}
阅读过30,更新下一篇!(下篇把这篇来不及写的都补上)