普通递归的改进(记忆性的递归)

1. 我们发现假如有时候使用递归或者深搜的时候数据量比较大的时候计算速度非常缓慢,而且可以发现最重要的是递归的时候存在重复子问题的重复求解了,比如像斐波拉契数列的求解f(n)  = f(n - 1) + f(n - 2)就存在着重复子问题的重复求解f(n - 1)继续递归下去的时候那么又会重复计算f(n - 2),这样假如数据量大的话耗时非常大,而且我们可以发现存在着像斐波拉契这样一类具有这样通项公式的递归形式问题的求解往往存在着很多重复子问题的重复求解,假如能够把计算过的子问题的值保存起来那么计算之前查询一下如果发现计算过那么直接返回了,不用搜索下去了

这样便会大大降低时间的复杂度,这个有点类似了深度优先搜索的剪枝,但是又有点同,dfs通常使用if条件来进行提前的剪枝,不满足条件的搜索那么接下来的支路都会剪断,而这里是递归之前进行查询,递归之后进行保存那么就可以达到解决我们重复子问题重复求解的问题了

像斐波拉契数列求解的过程:

普通递归的改进(记忆性的递归)_第1张图片

最后的f(2)递归调用f(1)和f(0)那么当递归调用退回来之后记录的是便是f(2)的值,再退回来记录的是f(3)的值,f(2)的值,f(4)的值...

记忆型的递归特别是针对重复子问题的重复求解的问题,当我们发现问题可以使用递归来求解,而且存在重复的子问题那么可以使用记忆型的递归来解决,这样便可以大大减少搜索的时间,像上面的f(5)的求解左边的递归完成之后都不需要进行右边的递归直接返回就可以了

斐波拉契数列记忆型的求解代码如下:

import java.util.Scanner;
public class Main{
    //使用一维数组来进行记录
    static int rec[];
    static int v1 = 0;
    static int v2 = 0;
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        rec = new int[n];
        //要先进行初始化
        rec[0] = 1;
        rec[1] = 1;
        int res = f(n - 1);
        System.out.println(res);
        for(int i = 0; i < n; i++){
            System.out.print(rec[i] + " ");
        }
        sc.close();
    }
    
    private static int f(int n){
        if(n == 0 || n == 1) return 1; 
        if(rec[n] > 0){
            //通过输出语句可以看到的确在求解重复子问题的时候查询之后发现之前求解过那么就会直接返回了
            System.out.println(rec[n]);
            return rec[n];
        }else{
            v1 += f(n - 1);
            //左边已经把f(n - 1)计算出来了所以v2直接加上f(n - 2)就是当前调用f(n)结束之后f(n)的值了
            v2 = v1 + f(n - 2);
            //调用完之后那么进行f(n)的值的记录
            rec[n] = v2;
            return v2;
        }
    }
}

 

你可能感兴趣的:(贪心与动态规划)