目录
前言
从递归开始,可以说才真正迈入算法的大门。递归也是最经典的算法之一
递归
程序调用自身的编程技巧称为递归( recursion)。递归作为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
这是一个家喻户晓的故事,同时也是递归的典型例子:
从前有座山,山里有座庙,庙里有个老和尚在给小和尚讲故事,讲的什么故事呢?他讲的是:从前有座山,山里有座庙,庙里有个老和尚在给小和尚讲故事,讲的什么故事呢?他讲的是:从前有座山,山里有座庙,庙里有个老和尚在给小和尚讲故事,讲的什么故事呢?他讲的是:从前有座山,山里有座庙,庙里有个老和尚在给小和尚讲故事,讲的什么故事呢?........
如果说,你还不懂什么是递归,那么请重复看这句话,直到懂了为止
所以,也许现在就懂递归了,递归递归,也就是我们常说的套娃。
当然,以上的例子都是无限循环的,真正的递归是有终止条件的。
递归需要满足的三个条件:
(1)一个原始的问题,可以分解为多个子问题进行求解
(2)这个问题分解之后的子问题,除了数据不一样,其余过程一模一样
(3)存在终止条件
我们就把满足以上三个条件的程序称为递归算法
递归模板
int f(参数) {
if(终止条件) return 0;
else return f(相关参数)[相关运算];
}
求解递归的步骤
(1):确定递归函数的参数和返回值。是int类型的递归,还是long long还是double形态的
(2):确定终止条件。到适当的时候就结束递归,不要做一些无用功,时间该省就省。
(3):确定递归的逻辑,下一步该调用什么参数。
例如这里以阶乘为例:求5!
(1)首先5!的规模不大,所以采用int类型;
int factorial(int num) {
}
//首先搭好一个框架
(2)其次,我们知道5!=5*4*3*2*1,但是乘1没什么具体的用处,所以不妨跳过,那么5!=5*4*3*2
所以,终止条件就是为2的时候
int factorial(int num) {
if(num==2) return 2;
}
//写出终止条件
(3)最后,确定递归的逻辑,阶乘的下一次调用,就是减去1
int factorial(int num) {
if(num==2) return 2;
else return num*factorial(num-1);
}
记忆化递归
记忆化递归的例题:洛谷P1192 台阶问题
洛谷P1255 数楼梯,不过本题是高精记忆化递归,含金量挺高的
递推
递归懂了之后,接下来就是递推了。
递推算法是一种用若干步可重复运算来描述复杂问题的方法。递推是序列计算中的一种常用算法。通常是通过计算前面的一些项来得出序列中的指定项的值。
通俗来说,就是找规律。
以前高中的时候,做规律题,不就是从1到2,从2到3,.....这样下去,就发现规律了,从而得出数值为n的时候。
其实这个过程,也就是所谓的递推。
例如斐波那契数列,我们知道规律就是前两项之和,所以递推代码就是这样:
a[1]=a[2]=1;
for(int i=3;i<=n;i++) {
a[i]=a[i-1]+a[i-2];
}
同理,对于杨辉三角
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
........
我们知道规律,除开头和结尾都是1,我们知道每一项都是上面邻接两项之和
所以不难得出规律:a[ i ][ j ]=a[ i-1 ][ j-1 ]+a[ i-1 ][ j ]
所以递推代码也就是:
for(int i=0;i<=n;i++){
a[i][0]=1;a[i][i]=1;
for(int j=1;j<i;j++){
a[i][j]=a[i-1][j-1]+a[i-1][j];
}
}