斐波那契数组是我最早使用递归解决的问题,应该是在大一学习C语言时的课后作业。所谓斐波那契数组就是数组前两个数是1,之后的每个数都是前两个数字之和,使用递归算法的Java代码如下
public static int FB(int n){
if(n==1&&n==2){
return 1;
}
return FB(n-1)+FB(n-2);
}
哎想起来当年初学编程的日子了(快跑)。这个简单的递归算法却拥有极为恐怖的时间复杂度,大家粗略一算就可以得到是在指数级别的巨大时间开销 O(2^n)O(n)!! 之所以有这样巨大的开销咱们可以简单的推敲一下,比如说我要获得第20个斐波那契数,那么就要先得到18和19的,要想得到19的就需要先得到17的和18的,在这里18的斐波那契就递归调用了两回,形成了巨大的时间浪费
那么重点来了,这里我们引入了六大算法之动态规划法来优化斐波那契,在百度上有非常严谨专业的动态规划法的说明,在这里我来分享一下自己的粗浅理解。所谓的动态规划可以理解成为是解决重复子问题而导致计算重复的优化算法,所以首先是要有重复子问题,就是说原问题是可以根据分治算法分成若干规模较小性质相同的子问题的,符合这种条件(也就是递归)后,我们为了减少大量重复计算所以引入了dp数组来存放当前的最优解,这样递归时就可以直接调用dp数组中的值而不需要重复的递归计算。说起来干巴巴的只会越说越让人头大,下面看一下使用动态规划法来优化后的斐波那契代码
public static int Fibonaccidp(int n){
int[] dp = new int[n]; //来装入状态的最优解的数组
if(n==1||n==2){
return 1;
}
dp[0] = dp[1] = 1; //找到初始条件,这个初始条件就是递归的出口
for (int i = 2; i < n; i++) {
dp[i] = dp[i-1]+dp[i-2]; /*直接把算得的数字放入dp数组中,这样我们找第20个斐波那契时调用第19和18,而此时从第1个数字开始都已经放入了数组之中,找第19,18就不需要再递归计算*/
}
return dp[n-1];
}
当代码这样完成优化后时间复杂度直接降为线性O(n),跟之前的代码质量完全就是天上地下。在我以为这样的代码已经趋于完美时,我看到了论坛中大佬简化空间复杂度后的新代码。
public static int RealFibonaccidp(int n){
if(n==1||n==2){
return 1;
}
int pre = 1;
int cur = 1;
int sum = 0;
for (int i =2 ; i <n ; i++) {
sum = pre+cur;
pre = cur;
cur = sum;
}
return cur;
}
大牛认为每次计算时dp数组仅仅只会使用两个值,所以申请的空间被大量浪费,实际上只需要申请三个变量空间就可以完成斐波那契,除了赞叹大牛丰富的经验和精湛的技术,更是明白了自己才疏学浅,对于代码优化与算法学习还是任重道远