最近我也在学习动态规划,略有一点感触,赶忙记录下来,加深印象,也希望能在评论区看见更加好的讲法,共同进步。
如有不足之处,还请多多包涵
可以根据目录按需查看
Top
本人也翻过许多讲递归的博客,看到一句夺人眼球的话:
人理解迭代,神理解递归
哇,我也想成神诶
。
。
。
现在感觉自己快成神经了 。。。
坚持学!
本文的每段java代码,你们也可以copy到你们的ide上自行运行,看注释帮助进行理解。
何为动态规划
把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解
n的阶乘
阶乘在数学上的定义
主要看我代码里面的注释
public class n的阶乘 {
public static int 递归阶乘(int n){
//自顶向下,我们已经知道了该怎么求,该分解n,求n*n-1*n-2*....*1
if(n==1){
return 1; //最后一个是 1 先设置好
}
return n*递归阶乘(n-1);
// 分析,第一次return是:就是5*阶乘(4)
// 第二次return是:4*阶乘(3)
// 第三次return是:3*阶乘(2)
// 第四次return是:2*阶乘(1)
// 回带:
// 第四次的return 2 作为第三次阶乘(2)的值
// 第三次的return 6 作为第二次阶乘(3)的值
// .....
// 第一次,最开始的return就是最后的值了 120
}
public static int DP阶乘(int n){
//自底向上,那就是需要我们验证,我们通过假设一种阶乘的定义叫n! 开始实验证明
//2!=1*2=2 //3!=1*2*3=6 每次记录下来步骤和值,发现n的阶乘的值都可以由前面一个阶乘的值*n本身
// 最后大佬在数学界给出定义,n的阶乘的定义应该是由1*2*....*n
int []rst = new int[n+1];
// 定义边界值
if(n<=1){
return 1;
}
rst[0] = 1; //数组下标从0开始,所以让rst[0]=1,好让rst[1]=1,所以创建数组时需要n+1的空间
for(int i=1;i<rst.length;i++){
rst[i]=rst[i-1]*i;
}
return rst[n];
//每一个过程都是由小至大,
// 先记录1*1的值(1) 存在rst[1],
// 再记录1*2的值(2) 存在rst[2],
// 再记录2*3的值(6) 存在rst[3],
// 再记录6*4的值(24) 存在rst[4],
// 再记录24*5的值(120) 存在rst[5] 所以创建数组时用n+1
}
public static void main(String[] args) {
//递归阶乘
System.out.println(n的阶乘.递归阶乘(5));
System.out.println(n的阶乘.DP阶乘(5));
}
}
n的阶乘的算法还是很简单的,容易让人理解到缩小问题规模是怎么样的一个过程。两个算法的注释也很清晰的表述了DP和递归最大的区别。
来看下一个例子,将更好的帮助你理解DP记忆结果的优势,避免重复计算
斐波那契数列
先来看斐波那契数列的算法定义
下面的代码中写了斐波那契数列的递归算法,动态规划的自顶向下和自底向上的三种算法。
重点还是要看注释,跟着注释一行行看代码
public class 斐波那契数列 {
// f(1)=1
// f(2)=1
// f(n)=f(n-1)+f(n-2)
// 看下面的算法就应该比较明显了,自顶向下,直接用公式递归
public static int 递归Fbnq(int n){
if(n<=2){
return 1;
}
return 递归Fbnq(n-1)+递归Fbnq(n-2);
// 分析:
// 递归Fbnq(5)return 递归Fbnq(4)+递归Fbnq(3)
// 递归Fbnq(4)return 递归Fbnq(3)+递归Fbnq(2)
// 递归Fbnq(3)return 递归Fbnq(2)+递归Fbnq(1)
// 开始回带:
// 递归Fbnq(3)=2
// 递归Fbnq(4)=3
// 递归Fbnq(5)=5
// 所以返回值为 5
}
public static int Fibonacci(