【数据结构与算法】之递归算法(上)

简介: 【数据结构与算法】之递归算法(上)

学习递归之前,我们可以首先思考一下“递推”这个概念?

因为,人的思想是更适合 【递推】 而不是 【递归】。


一、斐波那契数


1️⃣递推

我们举一个小例子,给出下列的一组数据,那么第10个数字是多少?


1,1,2,3,5,8....


正常人的思维肯定是,从前边的数据总结规律,然后从前向后,也就是“自低向上”寻求解决问题的思路,这个过程就是 【递推】 的过程,代码如下:

// 我们传入的n是从1开始计算的,第五个
private static int fibonacci1(int n) {
    // 创建一个数组,用来存放结果
    int[] result = new int[n];
    result[0] = result[1] = 1;
    // 从第三项开始递推,知道第n-1
    for(int i = 2 ; i <= n-1 ; i++){
        result[i] = result[i-1] + result[i-2];
    }
    return result[n-1];
}

时间复杂度: O(n)。


空间复杂度: O(n)。


2️⃣递归


递归的思路恰恰是相反的,递归的思路是要明确,我们要计算第九个,只要知道第7个和第8个就可以了,以此类推,想要知道第7个就只需要知道第6个和第5个就可以了,一直进行推算直到需要知道第一个和第二个为止。


其实我们可以给这个递推过程推导出一个方程如下,我们可以把他解释为”状态转移方程“:


f(n)={  1,n=1,2 f(n−1)+f(n−2),n>2


我们甚至可以画出如下的图形:


916146f2ba5545c8bb300b980d2f05c4.png


我们可以专门定义一个函数fibonacci2(int n),这个函数就是用来求第n个斐波那契数列的值的函数,代码如下:

private static int fibonacci2(int n) {
    if (n == 1 || n == 2)
        return 1;
    return fibonacci1(n - 1) + fibonacci1(n - 2);
}
f(n) = f(n-1) + f(n-2) = f(n-2) + f(n-3) + f(n-3) + f(n-4),每一项都裂变出两项,最终得出结论:O(2^n)

3️⃣重复子问题


我们再一次看看上边的图,会发现一个问题,很多的计算都是重复的,不多说,仅仅是上图中的内容,f(7)出现了3次,f(8)出现了两次,大量的计算是很消耗资源的,那有没有什么办法防止这些重复的计算呢?


我们可以使用一个备忘录,进行存储,每次计算完成之后将结果保存在一个数组(集合)中,代码如下:

// 使用一个数组memo进行保存,memo的下标代表第几个,值就是结果
private static int fibonacci2(int[] memo,int n) {
    // 如果存在就直接返回
    if (memo[n] > 0){
        return memo[n];
    }
    if (n == 0 || n == 1){
        memo[n] = 1;
    } else {
        memo[n] = fibonacci2(memo,n-1) + fibonacci2(memo,n-2);
    }
    return memo[n];
}

时间复杂度为 O(n),每一次计算的结果都进行保存,相当于计算n个结果。


4️⃣性能


我们比较一下三种方法的性能:

public static void main(String[] args) {
    long start = System.currentTimeMillis();
    System.out.println(fibonacci1(40));
    long end = System.currentTimeMillis();
    System.out.println(end - start);
    start = System.currentTimeMillis();
    System.out.println(fibonacci2(new int[40],40));
    end = System.currentTimeMillis();
    System.out.println(end - start);
    start = System.currentTimeMillis();
    System.out.println(fibonacci3(40));
    end = System.currentTimeMillis();
    System.out.println(end - start);
}

e78e26456e92486687283a44992cbf79.png


我们会发现,当有了【备忘录】之后,性能得到了大幅度的提升,这就是所谓的【空间换时间】。


二、抢5游戏


两个人先从【数字1或2】中选择一个,然后,另一个人在这个基础上选择加1或者加2,正好加到5为止,如果是你怎么能保证这个游戏必赢呢?


这个问题很简单,我们把各种情况列出来,总能找到答案,因为一共就只有五个数字。


那换一个数字呢,比如20,你会发现,从递推的角度去思考很难得出答案,我先说2 然后…后面有非常多中情况 ,规则虽然很简单,但是这个思路确实不太行得通。此时递归的思路就来了,递归的思路是这个样子的:


-(1)我要是最后必须喊20,就必须让对手喊19或18。

-(2)我只要喊17,就可以让对手喊19或18,至于要倒数第二次喊17就行。

-(3)一次类推,只要我想喊17,上一次就必须是14,在上一次我就是11,以此类推 8 ,5,2

-(4)最后的结论就是只要我先喊2,然后依次5,8,11,14,17,20,我就必胜。


而这个思想就是一个递归的思想,递归的思想有两个明显的妙用:


-(1)只要解决了上一步的问题就能解决下一步的问题,一依次类推就能解决全部的问题。

-(2)推倒的过程是相同的,是可以复制的。


但是,使用递归一定要注意,过程相同,单要有结束条件。


规律: 倒着数,每次减3,最后的结果是2或1时停止:


我们可以下面代码:

public class Game {
    public static void main(String[] args) {
        List<Integer> five = getFive(20, new ArrayList<>());
        System.out.println(five);
    }
    private static List<Integer> getFive(int num,List<Integer> res){
        if (num > 0) {
            res.add(num);
            getFive(num - 3, res);
        }
        return res;
    }
}


结果如下:

f5442c2fc4a1488a8b83c29dbb19bce0.png


三、上台阶问题


一个人上台阶,每次只能上1个或2个台阶,问,有多少种情况可以上到第20个台阶?这个问题其实是斐波那契数列的应用。


前提条件是每次只能上一个或两个台阶,我们要上第20个台阶不外乎就是从第十八个或者第十九个台阶上,也就意味着上第十八个台阶的方式的数量+上第19个台阶的数量之和。


说的简单一点就是,我有x种情况可以上到第18个台阶,我有y种情况可以上到第19个台阶,那么上到第20个台阶的情况就有x+y种。


公式还是:f(n)=f(n-1)+f(n-2)。

相关文章
|
5天前
|
存储 监控 NoSQL
Redis处理大量数据主要依赖于其内存存储结构、高效的数据结构和算法,以及一系列的优化策略
【5月更文挑战第15天】Redis处理大量数据依赖内存存储、高效数据结构和优化策略。选择合适的数据结构、利用批量操作减少网络开销、控制批量大小、使用Redis Cluster进行分布式存储、优化内存使用及监控调优是关键。通过这些方法,Redis能有效处理大量数据并保持高性能。
23 0
|
3天前
|
缓存 算法 Java
数据结构~缓存淘汰算法--LRU算法(Java的俩种实现方式,万字解析
数据结构~缓存淘汰算法--LRU算法(Java的俩种实现方式,万字解析
|
4天前
|
机器学习/深度学习 算法 数据可视化
Python 数据结构和算法实用指南(四)(4)
Python 数据结构和算法实用指南(四)
10 1
|
4天前
|
机器学习/深度学习 存储 算法
Python 数据结构和算法实用指南(四)(3)
Python 数据结构和算法实用指南(四)
14 1
|
4天前
|
存储 算法 搜索推荐
Python 数据结构和算法实用指南(四)(2)
Python 数据结构和算法实用指南(四)
10 0
|
4天前
|
存储 算法 Serverless
Python 数据结构和算法实用指南(四)(1)
Python 数据结构和算法实用指南(四)
14 0
|
5天前
|
存储 算法 搜索推荐
Python 数据结构和算法实用指南(三)(4)
Python 数据结构和算法实用指南(三)
10 1
|
4天前
|
存储 搜索推荐 算法
Python 数据结构和算法实用指南(三)(3)
Python 数据结构和算法实用指南(三)
10 1
|
4天前
|
存储 算法 前端开发
Python 数据结构和算法实用指南(三)(2)
Python 数据结构和算法实用指南(三)
10 1
|
4天前
|
存储 算法 编译器
Python 数据结构和算法实用指南(三)(1)
Python 数据结构和算法实用指南(三)
13 1