【数据结构与算法】带你快速入门贪心算法

img

前言

在学习完基础的数据结构后,我们就要开始数据库和算法双开了,本系列是算法的第一篇博客,我们来讲贪心算法。学习算法前需要对前面的数据结构知识十分熟悉,我们会频繁使用到它们。不过也别担心,博主在讲算法前会带大家回顾之前数据结构的知识点的🤓


定义

贪心算法,与其说是算法,不如说成是贪心策略。这种策略是一种在每一步选择中都采取在当前状态下最好或最优的选择,从而“希望”导致结果是全局最好或最优的算法策略

当我们使用贪心算法时,解决问题的策略就变成了:从局部最优再到全局最优

做题步骤:

  1. 把解决问题的过程分为若干步(类似解数学题,得到答案前需要我们一一步步推导)
  2. 在解决每一步问题时,都选择当前看起来最优的解法(最能体现贪心的地方
  3. 最后 “希望” 得到全局最优解(在这里的”希望“是一个很暧昧的词)

举个简单的例子:在你面前摆放了一堆钞票,允许你拿走 10 张,如果想要达到最大的金额,你会怎么拿呢?

当然是每次都拿面额最大的钞票,最后的结果就是最大数额的钱


套路

像一些双指针、滑动窗口的题型,它们都有很明显的特征,知道一些题型的思路,就可以用套路去刷题,但是贪心没有固定的套路

很多贪心的题都是经验想出来的,或者是“想当然”的拍脑门法,因为贪心的核心就是:局部最优推出全局最优,全靠自己手动模拟,如果还不行,可能就需要动态规划

在很多题中我们可能也无意识地使用了贪心策略,只是我们没有发现而已,因为贪心算法本来就是下意识的操作,一种我们认为的最优解

而当我们写出本道题的贪心算法时,可以使用举反例来验证贪心的正确性。不过我们也不可能把所有的例子都列举完,此时就需要数学证明来验证,这个验证过程也是千题千面,很难说清


例题

1. 找零问题

题目:现在有一个顾客,在一个小卖铺里买了 4 块钱的东西,他给老板 50 块钱,老板就得找 46 块钱给顾客,此时老板手头上有面额为 20、10、5、1 的纸币,假设每个面额有无数张,现在我们想让老板用最少的张数完成找零,要怎么做?


思路分析

要怎么贪:把找零的过程分为若干步,一张张给顾客找,而且每一步都要用看起来最优的解法来找零。为了最快地凑够 46 块钱,每次找钱时都得找当前能找的最大的面额,因为这样才能使用最少的张数最快的时间凑够 46 块钱。第一张肯定要先找面额最大的,也就是 20 块,接下来凑 26 块钱,继续用面额 20 块钱找零,接下来就剩 6 块钱,再 5 最后 1,找零结束

代码

public class Main {
    public static void main(String[] args) {
        int[] denominations = {20, 10, 5, 1}; // 纸币面额数组
        int totalChange = 46; // 需要找零的总额

        findMinimumCoins(denominations, totalChange);
    }

    public static void findMinimumCoins(int[] denominations, int totalChange) {
        int count = 0; // 记录使用的纸币张数
        for (int i = 0; i < denominations.length; i++) {
            while (totalChange >= denominations[i]) {
                totalChange -= denominations[i];
                count++;
            }
        }

        System.out.println("最少需要使用 " + count + " 张纸币来完成找零。");
    }
}


2. 最小路径和

题目:如下图,小红圈要从左上角走到右下角,过程中只能向下走或者向右走,把路径上的数字加起来就是小红圈的路径和,要求找出最小路径和

image-20240611024722778


思路分析

小红圈移动的每一步就是若干步,要做到贪心,那我们就要让每一步都是当前看起来最优的选择,也就是选择数字最小的格子。这样我们就可以把贪心后的路线画出来(蓝色路线)

image-20240611025811245

依据上图所示,蓝色路线是我们通过贪心算法得出来最小路径和 1+1+6+1+1 = 10,而我们通过肉眼观察,明明红色路线才是真正的最小路径和 1+3+1+1+1 = 7,难道贪心还会出错?

本道题的答案的确是 7,在这里贪出来 10 的最小路径和就是错的。本题是一个典型的最短路径问题,通常是通过动态规划来解决。贪心算法在这个问题中不是最佳选择,因为它不能保证找到全局最优解。但是但是我们的贪心策略是没错的,我们就是严格按照 从局部最优再到全局最优 这个思路去做的

因为本题贪心算法算出来的结果是错的,因此就不展示错误代码了


3. 背包问题

题目:此时有一个背包,背包的最大容量是 8,每一个物品都有无穷多个(完全背包问题),试问要怎么装才能让价值最大呢?

image-20240611143214054


思路分析

本题中有很多限制条件:物品体积,物品价值、背包体积,要让背包所装的价值最大,我们可以从三个角度来考虑:

①只考虑物品体积:那就是疯狂装体积较小的物品,只看数量,因为装的多价值就有可能大。按照这个角度算出来的总价值为:1+1+1+1+1+1+1+1 = 8

②只考虑物品价值:那就是疯狂装价值最大的物品,装不下就找价值第二大的。按照这个角度算出来的总价值为:10+1+1+1 = 13

③考虑体积和价值:求出每个物品的单位体积 W S \frac{W}{S} SW,排完序后,取单位体积价值最大的,放不下就依次往下选。按照这个角度算出来的总价值为:10+1+1+1 = 13

但是,我们通过观察,可以发现:选择两个②号物品的总价值才是最大的:7+7 = 14,所以上面三种贪心策略都是错的。虽然是错的,但以上三种都是我们根据不同角度想出来的贪心策略,这点没错

因为本题贪心算法算出来的结果是错的,因此也不展示错误代码了


总结

通过上面的三个例子,我们可以发现贪心算法的真正含义:贪婪 + 鼠目寸光,不仅贪,而且只考虑眼前的利益,并不会考虑全局的最优解,而是通过眼前的最优解,来 ”希望“ 得到全局的最优解

最后我们再总结一下贪心算法的特点:

  1. 贪心策略的提出是没有标准和模板的,可能是通过常识、题意、经验甚至是瞎蒙的
  2. 贪心策略的正确性有待商榷的,所有正确的贪心策略都是需要证明的(也是贪心算法最难最恶心的地方)
    我们想说一个贪心策略是错的,还是可以通过举反例来证明它是错的,但是想证明是对的,那可难多了

结语

今天我们简单讲了一下贪心算法是什么,以及几道辅助理解的小题,答案是什么不重要,重点是理解贪心的内涵:每一步选择中都采取在当前状态下最好或最优的选择,从而“希望”导致结果是全局最好或最优的算法策略。后面我们还会讲解几道常见的贪心算法,敬请期待吧

希望大家能够喜欢本篇博客,有总结不到位的地方还请多多谅解。若有纰漏,希望大佬们能够在私信或评论区指正,博主会及时改正,共同进步!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值