算法: 动态规划

动态规划

通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题 。

1.有一座高度是10级台阶的楼梯,从下往上走,每跨一步只能向上1级或者2级台阶。要求用程序来求出一共有多少种走法

我们先来分析下这个问题,假设我们只差最后一步就走到10级台阶,由于每一步台阶只允许往上跨1级或2级,这时候会出现两种情况,第一种情况 是从第9级 走到 第10级,第二种情况是从第8级 走到 第10级。

那么我们再考虑一个问题,如果我们已知0到9级台阶的走法有X种,而0到8级的台阶走法有Y种,那么0-10级的走法有多少种?

10级的台阶的所有走法可以根据最后一步的不同分成两部分,第一部分的最后一步是从9级 到 10级,这部分的走法数量和9级台阶的走法数量是相等的,也是X

故 0 - 10级的走法数量为X+Y种

那么我们可以得出一个结论:从(0-10级台阶的走法数量) = (0 - 9级台阶的走法数量) + (0 - 8级台阶的走法数量)

为了方便表达 我们把10级台阶的走法数量简写成 F(10),此时 F(10)=F(9)+F(8)

以此类推 F(N) = F(N - 1) + F(N - 2) (N>=3)

动态规划中包含三个重要的概念:[最优子结构],[边界],[状态转移方程]

刚才我们分析出 F(10) = F(9) + F(8), 因此 F(9) 和 F(8) 是 F(10)的[最优子结构]

当只有 1级 台阶或 2级 台阶时,我们可以直接得出结果,无需继续简化。我们称 F(1) 和 F(2)是问题的[边界]。如果一个问题没有边界,将永远无法得出有限的结果

F(N) = F(N - 1) + F(N - 2) 是阶段与阶段之间的[状态转移方程]

所以我们大体的思路应该是这样的

    public static int getClimbingWays(int n){

        if(n == 0){
            return 0;
        }else if(n == 1){
            return 1;
        }else if(n == 2){
            return 2;
        }

        return getClimbingWays(n-1)+getClimbingWays(n-2);

    }

这里写图片描述

不难看出这是一颗二叉树,且这颗二叉树的高度 是 N-1,节点个数接近2的N-1次方。所以方法的时间复杂度可以近似的可以看作是 O(2^N)。

现在我们看一下上述的递归图,我们可以看出一个问题,有些相同参数被重复计算了,越往下走,重复的越多

这里写图片描述

如图,相同的颜色被代表传入相同的参数。

所以我们可以用备忘录算法来做个优化

大体思路:先创建一个哈希表,每次把不同参数的计算结果存入哈希。当遇到相同参数时,再从哈希表中取出来,这样我们就可以不用重复计算了


public static int getClimbingWays(int n,Map<Integer,Integer> map){
        if(n == 0){
            return 0;
        }else if(n == 1){
            return 1;
        }else if(n == 2){
            return 2;
        }
        if(map.containsKey(n)){
            return map.get(n);
        }else{
            int v = getClimbingWays(n-1,map)+getClimbingWays(n-2,map);
            map.put(n, v);
            return v;
        }

}

现在时间复杂度已经优化的差不多了O(N),我们现在看看能不能把空间复杂度O(N)近一步减小。

我们不妨把思路调转过来 F(10) = F(9) + F(8) , 而 F(1) 为 1, F(2) 为 2, F(3) = F(2) + F(1),F(4)=(F3)+F(2)

这里写图片描述

    public static int getClimbingWays2(int n){
        if(n == 0){
            return 0;
        }else if(n == 1){
            return 1;
        }else if(n == 2){
            return 2;
        }
        int a = 1;
        int b = 2;
        int temp = 0;

        for(int i=3; i <= n; i++){
            temp = a + b;
            a = b;
            b = temp;
        }

        return temp;

    }

现在的时间复杂度为O(N) , 空间复杂度为 O(1)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值