微信扫一扫打赏支持

算法与数据结构---6.7、递推和递归的关系

算法与数据结构---6.7、递推和递归的关系

一、总结

一句话总结:

递归元素之间的关系式就是递推表达式,或者说递推可以用递归来实现,当然递推也可以不用递归来实现,比如用普通循环来实现

 

 

1、递推和递归主要作用分别是什么?

递推主要指是找规律来找到递推表达式,从而求解问题,是一种解决问题的方式
递归主要指递归函数,递归函数是指自己调用自己的函数,是一种编程方式

 

 

 

二、斐波那契数列

博客对应课程的视频位置:6.7、递推和递归的关系
https://www.fanrenyi.com/video/27/279

 

1、题目描述

问题描述:

满足F1=F2=1,F(n)=F(n-1)+F(n-2)的数列称为斐波那契数列(Fibonacci),
它的前若干项是1,1,2,3,5,8,13,21,34,55...,求此数列第n项 mod 10^9+7的值(n>=3)。

输入格式:
一行一个正整数n

输出格式:
一行一个整数表示答案。

输入输出样例:
输入5,输出5
输入10,输出55

【数据范围】
对于60%的数据,1<=n<=92;
对于100%的数据,1<=n<2^63。


题目位置:
P1962 斐波那契数列 - 洛谷 | 计算机科学教育新生态
https://www.luogu.com.cn/problem/P1962

 

2、递推解法

 1 /*
 2 
 3 递推关系式:
 4 题目中已经非常明显的给出了,就是
 5 F(n)=F(n-1)+F(n-2)
 6 
 7 解决递推问题的一般步骤
 8 1、建立递推关系式:F(n)=F(n-1)+F(n-2)
 9 2、确定边界条件:
10 f(1)=f(2)=1,
11 所以我们的循环可以从3开始,到n结束,
12 也就是3-n
13 
14 算法步骤:
15 1、确定初始值
16 2、循环做递推,3-n
17 
18 */
19 #include <iostream>
20 using namespace std;
21 const int mod=1000000007;
22 int f[200000];
23 int main(){
24     int n;
25     cin>>n;
26     //1、确定初始值
27     f[1]=f[2]=1;
28     //2、循环做递推,3-n
29     for(int i=3;i<=n;i++){
30         //F(n)=F(n-1)+F(n-2)
31         f[i]=(f[i-1]+f[i-2])%mod;
32     }
33     cout<<f[n]<<endl;
34     return 0;
35 }

 

 

3、滚动数组优化

 1 /*
 2 
 3 之前的最大子段和的动态规划的优化的时候,
 4 我们讲了滚动数组优化,
 5 原因是 对应的状态转移方程为:
 6 f[i]=max(f[i-1]+a[i],a[i]) (2<=i<=n)
 7 里面只用到了f[i]和f[i-1]这两个元素,
 8 所以可以用只有两个元素的数组来优化
 9 
10 我们现在的递推表达式是:
11 f[i]=f[i-1]+f[i-2] (3<=i<=n)
12 里面用到了f[i]、f[i-1]和f[i-2]三个元素,
13 所以可以用含有三个元素的数组来优化
14 
15 滚动数组的代码修改也很简单
16 直接在递推表达式有i的位置%3即可
17 f[i%3]=f[(i-1)%3]+f[(i-2)%3];
18 (%3是因为现在是有三个元素的滚动数组)
19 
20 注意:
21 取结果的时候,n也需要模3,例如f[n%3]
22 
23 */
24 
25 #include <iostream>
26 using namespace std;
27 const int mod=1000000007;
28 int f[3];
29 int main(){
30     int n;
31     cin>>n;
32     //1、确定初始值
33     f[1]=f[2]=1;
34     //2、循环做递推,3-n
35     for(int i=3;i<=n;i++){
36         //F(n)=F(n-1)+F(n-2)
37         f[i%3]=(f[(i-1)%3]+f[(i-2)%3])%mod;
38     }
39     //注意n也需要模3
40     cout<<f[n%3]<<endl;
41     return 0;
42 }

 

 

4、递推和动态规划的关系

/*

上述代码也就是这个题目动态规划的写法

动态规划里面有状态,状态转移方程
递推里面初始值,递推表达式
其实动态规划里面的状态转移方程,就是递推表达式
动态规划里面的初始状态,就是递推里面的初始值

所以动态规划可以看做是一种特殊的递推,
动态规划可以看做保存中间状态(中间结果)的递推

对于这题:
状态可以设置为:f[i]为 斐波那契数列第n项 mod 10^9+7的值
那么状态转移方程就是递推表达式:F(n)=F(n-1)+F(n-2)
初始状态:f[1]=f[2]=1

*/

 

5、三个变量解法

 1 /*
 2 f[3] 可以直接用3个变量a、b、c来代替
 3 这个时候就不能通过取模来自动变换位置了
 4 
 5 
 6 */
 7 #include <iostream>
 8 using namespace std;
 9 const int mod=1000000007;
10 int main(){
11     int n;
12     int a,b,c;
13     cin>>n;
14     //1、确定初始值
15     //这里对a也赋值为1,是为了保证n=1和n=2的时候也有正确结果输出
16     c=a=b=1;
17     //2、循环做递推,3-n
18     for(int i=3;i<=n;i++){
19         //F(n)=F(n-1)+F(n-2)
20         c=(b+a)%mod;
21         //保留f(n)和f(n-1)做下一轮的f(n-1)和f(n-2)
22         a=b;
23         b=c;
24     }
25     cout<<c<<endl;
26     return 0;
27 }

 

 

6、递归写法

 1 /*
 2 
 3 本题递推法的递推的关系式非常明确,就是f[i]=f[i-1]+f[i-2]
 4 递推法的递推关系式,对应到递归,就是递归的各个元素之间的关系
 5 明确这个,递归的代码就特别好敲
 6 
 7 递归注意点
 8 递归的终止条件:n=2和n=1
 9 递归的递推表达式:f[i]=f[i-1]+f[i-2] (3<=i<=n)
10 递归的返回值:所求值(斐波那契数列第n项 mod 10^9+7的值)
11 
12 */
13 #include <iostream>
14 using namespace std;
15 const int mod=1000000007;
16 
17 int find(int n){
18    if(n==1||n==2) return 1;
19    else{
20        return (find(n-1)+find(n-2))%mod;
21    }
22 }
23 
24 int main(){
25     int n;
26     cin>>n;
27     cout<<find(n)<<endl;
28     return 0;
29 }

 

 

 

7、记忆化递归

 1 /*
 2 
 3 比如我们要求f[6]
 4 f[6]=f[5]+f[4]
 5 f[5]=f[4]+f[3]
 6 f[4]=f[3]+f[2]
 7 ...
 8 
 9 我们可以看到,在上述过程中,f[4]、f[3]等都出现了很多次,都被重复计算了很多次
10 这就是递归效率为什么不高的原因
11 
12 解决这个问题就用记忆化递归,就是把已经计算的中间状态保存下来,
13 下次需要的时候就直接拿这个结果,就不用重复计算了
14 
15 
16 记忆化递归的思想和动态规划的思想是一样的,
17 都是保存中间计算结果,避免重复计算,拿空间换时间
18 
19 
20 */
21 
22 #include <iostream>
23 #include <cstring>
24 using namespace std;
25 const int mod=1000000007;
26 
27 int cache[200000];
28 
29 int find(int n){
30     //就是如果缓存中有,就直接拿缓存
31     //否则计算,然后将计算的结果保存到缓存
32    if(cache[n]!=-1) return cache[n];
33    else{
34        return cache[n]=(find(n-1)+find(n-2))%mod;
35    }
36 }
37 
38 int main(){
39     int n;
40     cin>>n;
41     memset(cache,-1,sizeof(cache));
42     cache[2]=cache[1]=1;
43     cout<<find(n)<<endl;
44     return 0;
45 }

 

 

 

8、递推和递归的关系

 1 /*
 2 
 3 递推主要指是找规律来找到递推表达式,从而求解问题,是一种解决问题的方式
 4 递归主要指递归函数,递归函数是指自己调用自己的函数,是一种编程方式
 5 
 6 
 7 上面讲递归解法的时候,
 8 在讲递归注意点的时候,会讲到
 9 
10 递归的初始条件:
11 递归的递推表达式:
12 递归的返回值:
13 
14 我们在写递归代码的时候,也是直接用的递推表达式
15 
16 总结:
17 递归元素之间的关系式就是递推表达式
18 或者说 递推可以用递归来实现,
19 当然递推也可以不用递归来实现
20 
21 
22 */

 

 

 

 
posted @ 2020-06-09 08:47  范仁义  阅读(426)  评论(0编辑  收藏  举报