【算法排序】动态规划

简介: 【算法排序】动态规划

一、动态规划思想

将待求问题划分为若干个子问题,按划分的顺序求解子阶段问题,前一个子问题的解,为后一个子问题的求解提供了有用的信息(最优子结构)。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其它局部解。依次解决各个子问题,最后求出原问题的最优解

二、动态规划与分治法的区别

1、共同点

二者都是求原问题具有最优子结构性质,都是将原问题分而治之,分解成若干个规模较小的子问题,然后将子问题的解合并,形成原问题的解

2、不同点

1、分治法:将问题分解成子问题后,通常是将子问题的解合并起来得到原问题的解

2、动态规划法:是先解决子问题,将子问题的解保存下来,最后根据子问题的解计算得到原问题的解

三、动态规划特征

1、最优子结构

问题的最优解是由最优子问题的最优解推出的,也就是问题的最优解包含了子问题的最优解

如图:若路线I和J是A到C的最优路径,则根据最优化原理,路线J必是从B到C的最优路线。

反证法证明: 假设有另一路径J是B到C的最优路径,则A到C的路径取I和J比I和J更优,矛盾:从而证明J`必是B到C的最优路径。最优化原理是动态规划的基础,任何问题,如果失去了最优化原理的支持,就不可能用动态规划方法计算哪些问题满足最优化原理

2、重叠子问题

在用递归算法自顶向下解问题时,每次产生的子问题并不是总是新问题。有些子问题被反复计算多次。斐波那契数列问题就存在重叠子问题。在递归实现中,当计算F(n)时,会先计算F(n-1)和F(n-2),而计算F(n-1)时又会先计算F(n-2)和F(n-3),这样就会出现重复计算的情况。

四、动态规划求解问题的基本步骤

动态规划所处理的问题是一个多阶段决策的问题,一般由初始状态开始,通过中间阶段决策的选择,达到结束状态。动态规划算法的代码设计都有一定的模式。一般经过以下几个步骤:

初始状态->决策1->决策2->…->决策n->结束状态

  1. 找出最优解的性质,并刻画其结构特征(找问题状态)
  2. 递归地定义最优值(找状态转移方程)
  3. 自底向上地方式计算最优值
  4. 根据计算最优值时得到的信息,构造最优解

五、斐波那契数分析

斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0, F(1) = 1

F(N) = F(N - 1) + F(N - 2), 其中 N > 1.

给定 N,计算 F(N)。

序列为:0、1

下标为:2

解释:F(2) = F(N - 1) + F(N - 2)= F(2 - 1) + F(2 - 2)=1+0 = 1

下标2的值为:1

序列为:0、1、1

数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和(示意图如下)

六、实现思路

  1. 定义一个要求的斐波那契数
  2. 判断求的斐波那契数是否小于等于1,是的话直接返回求的斐波那契数,否的话,则创建一个数组,用来记录子问题的解
  3. 初始化第一个斐波那契数值,和第二个斐波那契数值。求第三个斐波那契数值的时候,把前两个下标的值进行相加,得出第三个斐波那契的数值。依次按照这种方式,最终得出需要求的斐波那契数值

七、代码实现

public class Fibonacci {
    public static void main(String[] args) {
        int numberArr = 10; // 求第10个斐波那契数
        int result = fibonacci(numberArr); // 第10个斐波那契数的值
        System.out.println("第10个斐波那契数值为:"+ result); // 输出第10个斐波那契数的值
    }
    /**
    * 斐波那契数列的动态规划算法
    * @paramn  第numberArr个斐波那契数,dp为数据处理后的值
    * @return 第numberArr个斐波那契数的值
    */
    public static int fibonacci(int numberArr) {
        if (numberArr <= 1) {
            // 当numberArr小于等于1时,直接返回numberArr
            return numberArr;
        }
        //创建数组,用于记录子问题的解
        //dp为数据处理后的值
        int[] dp = new int[numberArr + 1];
        dp[0] = 0; // 初始化第一个数
        dp[1] = 1; // 初始化第二个数
        // 递推求解子问题
        for (int i = 2; i <= numberArr; i++) {
            //dp[i]:i为该求下标的值
            //dp[i-1]:i为该求下标-1的值
            //dp{[i-2]:i为该求下标-2的值
            //算出两个下标的值后进行相加,最后得出该求下标的值
            dp[i] = dp[i - 1] + dp[i - 2];
            System.out.println("第"+i+"轮numberArr的值为"+ dp[i]);
        }
        // 返回第numberArr个斐波那契数的值
        return dp[numberArr];
    }
}


目录
打赏
0
0
0
0
1
分享
相关文章
|
2月前
|
算法系列之动态规划
动态规划(Dynamic Programming,简称DP)是一种用于解决复杂问题的算法设计技术。它通过将问题分解为更小的子问题,并存储这些子问题的解来避免重复计算,从而提高算法的效率。
43 4
算法系列之动态规划
JavaScript 中通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能,JS中排序算法的使用详解(附实际应用代码)
Array.sort() 是一个功能强大的方法,通过自定义的比较函数,可以处理各种复杂的排序逻辑。无论是简单的数字排序,还是多字段、嵌套对象、分组排序等高级应用,Array.sort() 都能胜任。同时,通过性能优化技巧(如映射排序)和结合其他数组方法(如 reduce),Array.sort() 可以用来实现高效的数据处理逻辑。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
6月前
|
深入了解动态规划算法
深入了解动态规划算法
131 1
【动态规划篇】穿越算法迷雾:约瑟夫环问题的奇幻密码
【动态规划篇】穿越算法迷雾:约瑟夫环问题的奇幻密码
【潜意识Java】蓝桥杯算法有关的动态规划求解背包问题
本文介绍了经典的0/1背包问题及其动态规划解法。
77 5
【排序算法】八大排序(下)(c语言实现)(附源码)
本文继续学习并实现了八大排序算法中的后四种:堆排序、快速排序、归并排序和计数排序。详细介绍了每种排序算法的原理、步骤和代码实现,并通过测试数据展示了它们的性能表现。堆排序利用堆的特性进行排序,快速排序通过递归和多种划分方法实现高效排序,归并排序通过分治法将问题分解后再合并,计数排序则通过统计每个元素的出现次数实现非比较排序。最后,文章还对比了这些排序算法在处理一百万个整形数据时的运行时间,帮助读者了解不同算法的优劣。
228 7
【排序算法】八大排序(上)(c语言实现)(附源码)
本文介绍了四种常见的排序算法:冒泡排序、选择排序、插入排序和希尔排序。通过具体的代码实现和测试数据,详细解释了每种算法的工作原理和性能特点。冒泡排序通过不断交换相邻元素来排序,选择排序通过选择最小元素进行交换,插入排序通过逐步插入元素到已排序部分,而希尔排序则是插入排序的改进版,通过预排序使数据更接近有序,从而提高效率。文章最后总结了这四种算法的空间和时间复杂度,以及它们的稳定性。
193 8

热门文章

最新文章