基础算法-递归&分治&回溯
基本概念
数论思想:利用数学公式或者定理或者规律求解问题。
算法中最难的是:递归(树论:二叉树,红黑树都是用递归)和动态规划
递归
递归是一个非常重要的算法思想,应用也是相当的广泛,包括我们后边学的数据结构尤其是树形结构里面跟递归是密布可分的。
数学公式:f(n)=f(n-1)+1
递归的时间复杂度和空间复杂度都很大所以对于递归需要优化
递归做了很多重复的计算导致时间复杂度很大
递归优化
1.使用非递归。所有的递归代码理论上都可以转换成非递归的
2.加入缓存:吧中间值运算结果保存起来,这样就可以把递归降为O(n)
3.尾递归:尾递归就是调用函数一定出现在末尾,没有任何其他操作,因为编译器在编译代码时,如果发现函数末尾已经没有操作了,这时候就不会创建新的栈,而且覆盖到前面去
(尾递归也就是倒着算)
斐波那契数列代码实现(递归及递归优化)
package rec;
import java.net.Inet4Address;
import javax.imageio.event.IIOReadWarningListener;
import javax.sound.midi.Soundbank;
import javax.xml.crypto.Data;
import org.omg.PortableInterceptor.INACTIVE;
//递归代码时间复杂度和空间复杂度都是:O(2^n)有待优化
public class Fibonacci {//斐波那契数列
private static int data[];//初始化全部是0
//1 1 2 3 5 8 13 ……
//f(n)=f(n-1)+f(n-2)
public static int fab (int n){
if (n<=2){
return 1; //递归终止条件
}
return fab(n-1)+fab(n-2); //继续递归的过程
}
//优化取消递归,所有递归都可以用循环转换
public static int noFab(int n){ //不用递归
//循环
if (n<=2){
return 1;
}
/*int data[]=new int [n];
data[1]=1;
data[2]=1;*/
int a=1;
int b=1;
int c=0;
for (int i=3;i<=n;i++){ //时间复杂度O(n)
c=a+b;
a=b;
b=c;
//data[i]=data[i-1]+data[i-2];
}
return c;
}
//优化2:用数组进行缓存,避免重复计算
public static int fab2(int n){ //时间复杂度降为O(n) 空间复杂度也降为O(n)
if (n<=2){
return 1;
}
if(data[n]>0){
return data[n];
}
int res=fab2(n-1)+fab2(n-2); //继续递归的过程
data [n]=res;
return res;
}
//优化3尾递归(只有递,没有归)
public static int tailfab(int pre,int res,int n){
if (n<=2){
return res;
}
return tailfab(res,pre+res,n-1);
/*n
* res当前运算的结果
* pre上次运算出的结果
*/
}
public static int fac(int n){//N的阶乘(普通递归)
if (n<=1)return 1;
return n*fac(n-1);
}
//尾递归
public static int tailfac(int n,int res){//尾递归就是传结果 res就是每次保存的结果
System.out.println(n+" "+res);
if (n<=1)return 1;
return tailfac(n-1, n*res);//相当于倒着算
}
public static void main(String[] args) {
tailfac(5, 1);
/*for (int i=1 ;i<=45;i++){
long start = System.currentTimeMillis();
System.out.println(i+": "+fab(i)+" 所花时间为:"+(System.currentTimeMillis()-start)+"ms");
}*/
/*for (int i=1 ;i<=45;i++){
long start = System.currentTimeMillis();
System.out.println(i+": "+ noFab(i)+" 所花时间为:"+(System.currentTimeMillis()-start)+"ms");
}*/
//加了缓存的
/*data =new int [46];
for (int i=1 ;i<=45;i++){
long start = System.currentTimeMillis();
System.out.println(i+": "+ fa b2(i)+" 所花时间为:"+(System.currentTimeMillis()-start)+"ms");
}
*/
for (int i=1 ;i<=45;i++){
long start = System.currentTimeMillis();
System.out.println(i+": "+ tailfab(1,1,i)+" 所花时间为:"+(System.currentTimeMillis()-start)+"ms");
}
}
}
总结:递归可以使代码看起来整洁可读性高,但是使用起来一定要注意栈溢出(跳出递归的条件)和时间复杂度的问题,不太清楚的情况下的使用for循环或者使用数组保存中间结果(递归的优化)