贪心法思想-求最大子数组和案例图解

贪心法思想

​ 基本思想是在问题的每个决策阶段,都选择当前看起来最优的选择,即贪心地做出局部最优的决策,以期获得全局最优解。

​ 正如其名字一样,贪心法在解决问题的策略上目光短浅,只根据当前已有的信息做出选择。一旦做出选择,不管将来有什么结果,这个选择都不会改变。

与动态规划对比:

​ 贪心算法和动态规划都常用于解决优化问题。它们之间存在一些相似之处,比如都依赖最优子结构性质,但工作原理不同。

  • 动态规划会根据之前阶段的所有决策来考虑当前决策,并使用过去子问题的解来构建当前子问题的解。
  • 贪心算法不会考虑过去的决策,而是一路向前地进行贪心选择,不断缩小问题范围,直至问题被解决。

举例-零钱兑换问题:

​ 贪心算法实现简单,易于理解,效率也很高,但是具有局限性。

有一组不同面额的硬币,给定一个总金额 𝑎𝑚𝑡 ,计算并返回可以凑成总金额所需的最少的硬币个数 。

每次贪心的选择尽可能大的硬币(不大于总金额的最大硬币),直至凑出目标金额为止。

硬币组合[100、50、10、5、1],总金额amt为131:

在这里插入图片描述

但是,某些情况下使用贪心是无法得到最优解的:

​ 硬币组合:[1,20,50],总金额 𝑎𝑚𝑡=60 ,贪心算法只能找到 50+1×10 的兑换组合,共计 11 枚硬币,但动态规划可以找到最优解 20+20+20 ,仅需 3 枚硬币。

分析:

​ 对于组合1、3、4,需要金额6,那么按照贪心法:4+1+1三个,而3+3两个即可。

​ 如果硬币组合间有倍数关系,那么较大的硬币可以使用多个较小的硬币组合,意味着能用较小硬币的任意数量组合(加起来大于较大硬币),都可以直接使用较大硬币+一定数量的较小硬币替代去减少数量,不会出现3+3等于6,而4和3无法组合出6的这种情况。

贪心法案例-求最大子数组和图解

问题描述:

​ 给一个整数数组 ,请找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

原始数组:

nums = [-2,1,-3,4,-1,2,1,-5,4]

蛮力法:

​ 直接遍历所有情况,将任意情况下的最优解记录下来,取最大的

 public static int solution(int[] data, int n) {
        if (n < 0 || (n == 1 && data[0] <= 0)) {
            return 0;
        }
        if (n == 1) {
            return data[0];
        }
        int maxSum = 0, thisSum;
        // 计算i到i,i到i+1,...,i到n的和,选出最大的一个子序列。  
        // 当i从0遍历到n-1时,则计算过了所有的子序列
        for (int i = 0; i < n; i++) {
            thisSum = 0;// i 表示子序列左侧的位置
            for (int j = i; j < n; j++) { // j 表示子序列右边的位置
                // 汇总 i到j 的所有数之和时,对比solution1发现
                // 其实每次求和不需要都从i位置开始,因为 i到k的和 = i到K-1的和 + data[k]
                thisSum += data[j];
                maxSum = Math.max(maxSum, thisSum);
            }
        }
        return maxSum;
    }
贪心法:

​ 如果当前的子序列和小于0,那么负数+任何数都会变小,所以应该舍弃掉当前已经选择的子序列。

​ 所以我们只需要考虑当前的最优解即可,不断向后遍历数组,直到遍历完数组就可以得到最大的子序列和。

1、初始化

在这里插入图片描述

2、下标0

​ 下标为0,此时当前最大值变为 0 + ( − 2 ) = − 2 0+(-2)=-2 0+(2)=2,然后结果取负无穷和-2之间较大的,即-2.

t h i s M a x S u b = 0 + ( − 2 ) = − 2 thisMaxSub = 0 + (-2) = -2 thisMaxSub=0+(2)=2

− 2 > 负无穷  推出   r e s u l t = − 2 -2>负无穷 ~~推出~~ result = -2 2>负无穷  推出  result=2

​ 因为-2为负数,一定会使后续的子序列和变小,直接舍弃。

− 2 < 0   推出   t h i s M a x S u b = 0 -2<0 ~~推出~~ thisMaxSub = 0 2<0  推出  thisMaxSub=0

在这里插入图片描述

3、下标1

​ 当前最大值变为 0 + 1 = 1 0+1=1 0+1=1,然后结果取-2和1之间较大的,即1.

t h i s M a x S u b = 0 + 1 = 1 thisMaxSub = 0 +1= 1 thisMaxSub=0+1=1

1 > − 2   推出   r e s u l t = 1 1>-2 ~~推出~~ result = 1 1>2  推出  result=1

在这里插入图片描述

3、下标2

​ 当前最大值变为 1 + ( − 3 ) = − 2 1+(-3)=-2 1+(3)=2,然后结果取1和-2之间较大的,即1.

t h i s M a x S u b = 1 + ( − 3 ) = − 2 thisMaxSub = 1 + (-3) = -2 thisMaxSub=1+(3)=2

1 > − 2   推出   r e s u l t = 1 1>-2 ~~推出~~ result = 1 1>2  推出  result=1

​ 因为-2为负数,一定会使后续的子序列和变小,直接舍弃。

− 2 < 0   推出   t h i s M a x S u b = 0 -2<0 ~~推出~~ thisMaxSub = 0 2<0  推出  thisMaxSub=0

在这里插入图片描述

4、下标3

​ 当前最大值变为 0 + 4 = 4 0+4=4 0+4=4,然后结果取1和4之间较大的,即4.

t h i s M a x S u b = 0 + 4 = 4 thisMaxSub = 0 + 4 = 4 thisMaxSub=0+4=4

4 > 1   推出   r e s u l t = 4 4>1 ~~推出~~ result = 4 4>1  推出  result=4

在这里插入图片描述

5、下标4

​ 当前最大值变为 4 + ( − 1 ) = 3 4+(-1)=3 4+1=3,然后结果取4和3之间较大的,即4.

t h i s M a x S u b = 4 + ( − 1 ) = 3 thisMaxSub = 4 + (-1) = 3 thisMaxSub=4+1=3

4 > 3   推出   r e s u l t = 4 4>3 ~~推出~~ result = 4 4>3  推出  result=4

在这里插入图片描述

6、下标5

​ 当前最大值变为 3 + 2 = 5 3+2=5 3+2=5,然后结果取4和5之间较大的,即5.

t h i s M a x S u b = 3 + 2 = 5 thisMaxSub = 3+2=5 thisMaxSub=3+2=5

5 > 4   推出   r e s u l t = 5 5>4 ~~推出~~ result = 5 5>4  推出  result=5

在这里插入图片描述

​ 最终得到最大值为6

    public static int execute(int[] data) {
        // result:存储最大值;thisMaxSub:存储当前最大值
        int result = Integer.MIN_VALUE, thisMaxSub = 0;
        for (int datum : data) {
            thisMaxSub += datum;
            result = Math.max(thisMaxSub, result);
            // tempMaxSub小于0的话,就可以直接舍弃掉,因为负数+任何数都会变得更小
            thisMaxSub = Math.max(thisMaxSub, 0);
        }
        return result;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值