核心思想:
递归思想编写的代码常常会导致效率底下的问题,而效率底下根本的原因就是递归调用执行过程模拟了整个递推过程,而这个过程很多是重复性的工作。而动态规划就是用来解决这部分重复性的工作的。具体的思想就是记录下重复执行的那部分代码的结果,当下次需要的时候直接调用,以此来提升效率。
举例说明:
写一个代码输入一个整n,求在斐波纳契数列中第n个数的值.
代码一:递归思想
package 动态规划;
import java.util.Scanner;
public class Fibonacci {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("输入一个整数:");
System.out.print(fib(sc.nextInt()));
}
public static int fib(int n){
if(n<=2){
return 1;
}else{
return fib(n-1)+fib(n-2);
}
}
}
分析效率:
我们发现除了红色部分以外,其他的都是重复性的工作,就效率而言简直就是一场灾难。
代码二:动态规划——自顶向下动态规划
package 动态规划;
//自顶向下
import java.util.Scanner;
public class Fibonacci1 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入数字:");
int n = sc.nextInt();
System.out.println(Fib(n));
}
public static int Fib(int n){
int arr[] = new int[n+1];
for(int i=0;i<=n;i++){ //初始化数组
arr[i]=-1;
}
return fib(n,arr);
}
public static int fib(int n,int arr[]){
if(arr[n]!=-1){ //判断是否之前有执行过,如果有就直接返回。
return arr[n];
}if(n<=2){
return arr[n]=1;
}
else{
return arr[n] =fib(n-1,arr)+fib(n-2,arr);
}
}
}
分析效率:动态规划很好理解,当整个程序在执行fib(6)的时候,它会进而执行fib(5)和fib(4),当执行fib(5)的时候会执行fib(4)和fib(3),当fib(5)执行完的时候,就已经执行了fib(4)了并且已经保存在了arr[4]里面,当再执行fib(4)的时候程序判断出fib(4)的值后,就放弃继续执行直接进行fib(5)+fib(4)。
代码三:动态规划——自下向上
package 动态规划;
import java.util.Scanner;
//自底向上的动态规划
public class Fibonacci2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入一个整数:");
System.out.println(fib(sc.nextInt()));
}
public static int fib(int n){
if(n<0)
return 0;
int Memo[] = new int[n+1];
Memo[0] = 0;
Memo[1] = 1;
for(int i=2;i<=n;i++){
Memo[i]=Memo[i-1]+Memo[i-2];
}
return Memo[n];
}
}
分析效率:第一种动态规划其实还是使用了递归调用只不过是省去了计算开销,其他的额外开销还是有的。因此第二种直接自低向上的相对会更高效,切相对来说代码更简介。