斐波那契数列——普通递归、尾递归、记忆化搜索、动态规划

  • 普通递归(自顶向下)

    求解F(n),先求解F(n-1)+F(n-2),大量重复计算

    时间复杂度:O(2^n)

    空间复杂度:O(n)

    int fib(int n){
    		if(n == 0){
    			return 0;
    		}
    		
    		if(n == 1){
    			return 1;
    		}
    		
            //避免溢出,返回的结果都模1000000007
    		return fib(n-1) % M + fib(n-2) % M;  
    	}
    
  • 尾递归(自底向上)

    尾递归的一个特点是在回归过程中不用做任何操作,当编译器检测到一个函数使用尾递归的时候,它就覆盖当前的活动记录而不是在栈中去创建新的,这样栈空间大大缩减,从而提升运行效率。

    并且利用了每次递归调用时,都会把之前已经计算好的结果以参数的形式传递过去,避免重复计算

    时间复杂度:O(n)

    空间复杂度:O(n)

    int fib_1(int n, int a, int b){  //a是第n项的值,b是第n+1项的值 
    		if(n == 0){
    			return 0;
    		}
    		
    		if(n == 1){  //从F(1)、F(2)开始 
    			return a;
    		}
    		return fib_1(n-1,b % M,(a+b) % M);  //调用时的a,b的初值是1 1 
    } 
    
  • 记忆化搜索(自顶向下)

    依然是自顶向下的方法,额外开辟一个数组用于保存的计算过的值,使用递归实现,但是避免了重复计算,已经存在存在的值直接返回,计算的都是需要的值

    时间复杂度:O(n)

    空间复杂度: O(n)

    	int fib_2(int n){ 
    		if(n == 0){
    		    return 0;
    	    }
    	    if(n == 1 || n == 2){
    		    return 1;
    	    } 
    	    
    		if(memory[n] == 0){  //已经计算过的直接返回 
    	    	memory[n] = fib_2(n-1) % M  + fib_2(n-2) % M;
    	    } 
       	    return memory[n];  
    	} 
    
  • 动态规划

    初始状态: dp[0] = 0 dp[1] = 1, 定义状态转移方程:dp[n] = dp(n-1) + dp[n-2]

    时间复杂度:O(n)

    空间复杂度:O(n)

    int fib_3(int n){
    		if(n < 0){
    			return -1;
    		}
    		memory[0] = 0;
    		memory[1] = 1;
    		for(int i = 2;i <= n;i++){
    			memory[i] = memory[i-1] % M + memory[i-2] % M;   
    		}
    		return memory[n];
    	} 
    
  • 动态规划的空间复杂度优化

    使用三个变量交替前进,代替了使用一个dp数组,将空间复杂度降到O(1),时间复杂度还是O(n)

    int fib_4(int n) {
        	if(n == 0){
        		return 0;
    		}
    		int temp,a1=1,a2=1;  //从n==1开始计算 
            for(int i = 1;i < n;i++){ 
            	temp = a2;
            	a2 = (a1+a2) % M;  //加的是下一次的结果 
            	a1 = temp;  //返回的是上次加的结果 
    		}
    		return a1;
        }
    
posted @ 2021-03-25 15:54  简约的信仰  阅读(164)  评论(0编辑  收藏  举报