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

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

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

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


一、斐波那契数


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)。

相关文章
|
13天前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
23 1
|
2月前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
90 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
14天前
|
存储 算法 搜索推荐
Python 中数据结构和算法的关系
数据结构是算法的载体,算法是对数据结构的操作和运用。它们共同构成了计算机程序的核心,对于提高程序的质量和性能具有至关重要的作用
|
13天前
|
数据采集 存储 算法
Python 中的数据结构和算法优化策略
Python中的数据结构和算法如何进行优化?
|
21天前
|
算法
数据结构之路由表查找算法(深度优先搜索和宽度优先搜索)
在网络通信中,路由表用于指导数据包的传输路径。本文介绍了两种常用的路由表查找算法——深度优先算法(DFS)和宽度优先算法(BFS)。DFS使用栈实现,适合路径问题;BFS使用队列,保证找到最短路径。两者均能有效查找路由信息,但适用场景不同,需根据具体需求选择。文中还提供了这两种算法的核心代码及测试结果,验证了算法的有效性。
82 23
|
21天前
|
算法
数据结构之蜜蜂算法
蜜蜂算法是一种受蜜蜂觅食行为启发的优化算法,通过模拟蜜蜂的群体智能来解决优化问题。本文介绍了蜜蜂算法的基本原理、数据结构设计、核心代码实现及算法优缺点。算法通过迭代更新蜜蜂位置,逐步优化适应度,最终找到问题的最优解。代码实现了单链表结构,用于管理蜜蜂节点,并通过适应度计算、节点移动等操作实现算法的核心功能。蜜蜂算法具有全局寻优能力强、参数设置简单等优点,但也存在对初始化参数敏感、计算复杂度高等缺点。
56 20
|
21天前
|
机器学习/深度学习 算法 C++
数据结构之鲸鱼算法
鲸鱼算法(Whale Optimization Algorithm,WOA)是由伊朗研究员Seyedali Mirjalili于2016年提出的一种基于群体智能的全局优化算法,灵感源自鲸鱼捕食时的群体协作行为。该算法通过模拟鲸鱼的围捕猎物和喷出气泡网的行为,结合全局搜索和局部搜索策略,有效解决了复杂问题的优化需求。其应用广泛,涵盖函数优化、机器学习、图像处理等领域。鲸鱼算法以其简单直观的特点,成为初学者友好型的优化工具,但同时也存在参数敏感、可能陷入局部最优等问题。提供的C++代码示例展示了算法的基本实现和运行过程。
43 0
|
2月前
|
机器学习/深度学习 存储 缓存
数据结构与算法学习十:排序算法介绍、时间频度、时间复杂度、常用时间复杂度介绍
文章主要介绍了排序算法的分类、时间复杂度的概念和计算方法,以及常见的时间复杂度级别,并简单提及了空间复杂度。
38 1
数据结构与算法学习十:排序算法介绍、时间频度、时间复杂度、常用时间复杂度介绍
|
21天前
|
算法 vr&ar 计算机视觉
数据结构之洪水填充算法(DFS)
洪水填充算法是一种基于深度优先搜索(DFS)的图像处理技术,主要用于区域填充和图像分割。通过递归或栈的方式探索图像中的连通区域并进行颜色替换。本文介绍了算法的基本原理、数据结构设计(如链表和栈)、核心代码实现及应用实例,展示了算法在图像编辑等领域的高效性和灵活性。同时,文中也讨论了算法的优缺点,如实现简单但可能存在堆栈溢出的风险等。
35 0
|
2月前
|
存储 算法 Java
Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定数据结构和算法确保元素唯一性
Java Set因其“无重复”特性在集合框架中独树一帜。本文解析了Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定数据结构和算法确保元素唯一性,并提供了最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的hashCode()与equals()方法。
40 4