Java——贪心算法

贪心算法可以认为是动态规划算法的一个特例,相比动态规划,使用贪心算法需要满足更多的条件(贪心选择性质),但是效率比动态规划要高

比如说一个算法问题使用暴力解法需要指数级时间,如果能使用动态规划消除重叠子问题,就可以降到多项式级别的时间,如果满足贪心选择性质,那么可以进一步降低时间复杂度,达到线性级别的。

什么是贪心选择性质呢,简单说就是:每一步都做出一个局部最优的选择,最终的结果就是全局最优。 注意哦,这是一种特殊性质,其实只有一部分问题拥有这个性质。

比如你面前放着 100 张人民币,你只能拿十张,怎么才能拿最多的面额?显然每次选择剩下钞票中面值最大的一张,最后你的选择一定是最优的。

一定要注意判断问题是否适合采用贪心算法策略,找到的解是否一定是问题的最优解。如果确定可以使用贪心算法,那一定要选择合适的贪心策略; 选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。

一般能用贪心法解决的问题都有如下特性:

  • 存在最优度量标准,指所求问题的整体最优解可以通过一系列局部最优解的选择,即最优度量标准来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
  • 有最优子结构特性。当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题有最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。

例题:

1 纸币找零问题

假设1元、2元、5元、10元、20元、50元、100元的纸币,张数不限制,现在要用来支付K元,至少要多少张纸币?

很显然,我们很容易就想到使用贪心算法来解决,并且我们所根据的贪心策略是,每一步尽可能用面值大的纸币即可。当然这是正确的,代码如下:

public static void greedyGiveMoney(int money) {
        System.out.println("需要找零: " + money);
        int[] moneyLevel = {1, 5, 10, 20, 50, 100};
        for (int i = moneyLevel.length - 1; i >= 0; i--) {
            int num = money/ moneyLevel[i]; //需要找回当前面值纸币num张
            int mod = money % moneyLevel[i];//还需要找回多少钱
            money = mod;
            if (num > 0) {
                System.out.println("需要" + num + "张" + moneyLevel[i] + "块的");
            }
        }
    }

(1) 如果不限制纸币的金额,那这种情况还适合用贪心算法么。比如1元,2元,3元,4元,8元,15元的纸币,用来支付K元,至少多少张纸币?

经我们分析,这种情况是不适合用贪心算法的,因为我们上面提供的贪心策略不是最优解。比如,纸币1元,5元,6元,要支付10元的话,按照上面的算法,至少需要1张6元的,4张1元的,而实际上最优的应该是2张5元的。

(2) 如果限制纸币的张数,那这种情况还适合用贪心算法么。比如1元10张,2元20张,5元1张,用来支付K元,至少多少张纸币?

同样,仔细想一下,就知道这种情况也是不适合用贪心算法的。比如1元10张,20元5张,50元1张,那用来支付60元,按照上面的算法,至少需要1张50元,10张1元,而实际上使用3张20元的即可;

(3) 所以贪心算法是一种在某种范围内,局部最优的算法。

2 贪心算法可用来解决普通背包问题,0-1背包由动态规划来解决;

来源:牛客网

3 左右最值最大差(贪心算法)

给定一个长度为N(N>1)的整型数组A,可以将A划分成左右两个部分,左部分A[0…K],右部分A[K+1…N-1],K可以取值的范围是[0,N-2]。求这么多划分方案中,左部分中的最大值减去右部分最大值的绝对值,最大是多少?
给定整数数组A和数组的大小n,请返回题目所求的答案。

测试样例:
[2,7,3,1,1],5
返回:6
链接:左右最值最大差

基于贪心算法的思想这两个数中有一个肯定是数组的最大值。要使得差值最大,那么另一边的最大值应尽可能的小。假设最大值在左边,那么对于最大值右边的数组有很多种分法,每一种分法肯定都包含数组最后一个数字即A[n-1]。如果不取A[n-1],取最后一个数字和最大值中间的任一数字A[i]。若A[i]大于A[n-1],那还不如取最后一个数字;若最A[i] 小于A[n-1],那右半边的最大值肯定不是A[i],所以无论如何右半边取最右端数字。假设最大值在右边,同理左半边取最左端数字。只需用数组最大值减去数组两端较小的那个值即可。

  • 先找到最大值
  • 再拿最大值和两端的值进行相减就行了.
import java.util.*;
public class MaxGap {
    public int findMaxGap(int[] A, int n) {
        int max=0;
        for(int i=0;i<A.length;i++) { //找出数组中的最大值
            if(A[i]>max)
                max=A[i];
        }
        int ans1=max-A[0];
        int ans2=max-A[n-1];
        if(ans1>ans2)
            return ans1;
        else
            return ans2;
    }
}

参考博客:Java-贪心算法

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值