在处理算法问题时候,用的非常多的一种策略就是递归算法了。但是递归算法虽然简单有效,但是该算法的算法效果总是有点差强人意。本文主要讲述从两个方向优化递归算法,希望本文能给读者一些thinking。(持续更新中…)
示例:递归算法我又把它称为迭代算法,因为大致思路都差不多(迭代:从已知推未知 递归:从未知回溯到已知,再推未知)。都是不断地重复若干个较为简单的问题,最终得到理想结果的一种算法思路。(下文中并未严格区分递归和迭代)
首先先拿斐波那契数列来举例说明(示例):
public int Fibonacci(int n){ if(n<0)return 0; if(n==0)return 1; if(n==1)return 1; return Fibonacci(n-1)+Fibonacci(n-2); }此时该算法的公式为:F(n)=F(n-1)+F(n-2)+1,对应的时间复杂度为O(2^n)。具体可参考 https://blog.csdn.net/beautyofmath/article/details/48184331。 而造成这算法的时间复杂度如此之高的原因就是存在大量的冗余计算。其中F(n)被计算1次,F(n-1)被计算一次,F(n-2)被计算两次,Times(F(n-3)) = Times(F(n-1))+Times(F(n-2))… 若想提升这类算法的效率的话,必不可少的就是减少这些冗余计算。而想解决冗余此时需要读者想到“空间换时间、时间换空间”这个策略了。本算法所借助的就是“空间换时间”策略。
代码如下(示例):
public int Fibonacci(int n) { if(n <= 0) { return 0; } if(n==1 || n==2){ return 1; } int []ret = new int[n + 1]; ret[0] = 0; ret[1] = 1; for(int i = 2; i <= n; i++) { ret[i] = ret[i - 1]+ret[i - 2]; } return ret[n]; }本优化算法就是采用空间换时间的思想解决斐波那契高冗余计算。并且由Times(F(n-2)) = Times(F(n))+Times(F(n-1))可知,越底层的计算,其迭代次数越高 主要思路:将高冗余的属性加入到一个数组中。用O(1)->O(n)的空间复杂度代价,换取时间复杂度从O(2^n)->O(n)
实现 pow(x, n) ,即计算 x 的 n 次幂函数。代码示例:
public static double myPow(double x, int n) { if(x==0)return 0; if(x==1)return 1; if(n==0)return 1; if(n>0){ return myPow2(x,n); }else{ return 1/myPow2(x,-n); } } public static double myPow2(double x, int n) { if(n==1)return x; return x*myPow2(x,n-1); }此时该算法的公式为:F(n)=F(n-1)+1,对应的时间复杂度为O(n)。 此时该问题并不存在冗余计算的问题,无法使用动态规划思想。并且使用动态规划解决问题的最小时间复杂度都需要O(n)。但是此时的时间复杂度已为O(n)。若优化则量级上只能将其优化为O(log n)或者O(log log n)。而若提到O(log n)的时间复杂度,则必须想到二分查找算法。本算法的优化策略即由此展开的。
优化代码如下(示例):
public double myPow(double x, int n) { if(x==0)return 0; if(x==1)return 1; if(n==0)return 1; if(n==1)return x; if(n==-1)return 1/x; double half = myPow(x,n/2); double rest = myPow(x,n%2); return rest*half*half; }本优化算法采用二分策略跳过无用计算从而提升效率。 主要思路:因为本题中F(n)不止和F(n-1)有直接关系,和F(n/2)、F(n/3)也有之间关系。因此针对这种问题可以采用二分策略提升算法效率。此时时间复杂度从O(n)->O(log n)
上文是针对迭代算法的优化策略:动态规划和“二分策略”。 动态规划思想:动态规划针对求解的问题可以划分为一系列相互联系的阶段, 在每个阶段都需要作出决策,且一个阶段决策的选择会影响下一个阶段的决策, 从而影响整个过程的活动路线, 求解的目标是选择各个阶段的决策使整个过程达到最优。 前提条件:但是使用动态规划处理有一重要:“该问题需要满足最优子结构”。 而迭代问题大都满足此条件,因此若需要针对递归问题的优化策略。首推动态规划。 . . 二分策略:在需要将算法从O(n^2)->O(nlog n)或者从O(n)->O(log n)时候。此时一定要想到二分策略。而针对递归问题时候,可以思考F(n)是否和F(n/2)直接有关;若直接有关,那恭喜。二分策略一定能给你一个满意的优化方案。