贪心算法:看似精明的 “短视选手”,用好了也能逆袭!💥

想象你走进一家超豪华自助餐厅,面对琳琅满目的美食,每盘只能拿一份。你会怎么做?先抢最贵的龙虾?还是先拿一堆小蛋糕垫肚子?这种 "每次都选当前看起来最好" 的决策方式,就是编程界超有名的贪心算法!它简单粗暴,却又充满争议,今天咱们就来揭开它的真面目!

啥是贪心算法?自助餐式的 "精明决策"

贪心算法的核心思想就一句话:每次都选当前状态下最有利的选项,不考虑之后的情况。就像吃自助餐时,你不管后面还有没有更美味的,先把眼前的牛排、三文鱼拿了再说。在编程里,它常用于解决一些优化问题,比如如何用最少的硬币凑出指定金额,或者怎样在最短时间内完成多个任务。

不过,贪心算法有个致命弱点 ------目光短浅!它只看眼前利益,不考虑长远影响,就像你一口气吃撑了牛排,结果后面的帝王蟹只能眼巴巴看着。所以用它解题,有时候能 "躺赢",有时候却会翻车。

哪些情况能用贪心算法?这 3 类问题超合适!

1. 找零钱问题:硬币的 "最优组合"

假设你是收银员,顾客给了 100 元,商品价格是 63 元,现在要用最少的硬币找零(假设有面值为 25、10、5、1 元的硬币)。用贪心算法超简单:

ini 复制代码
public class CoinChange {
    public static int coinChange(int[] coins, int amount) {
        int count = 0;
        // 从大到小排序硬币面值
        java.util.Arrays.sort(coins);
        for (int i = coins.length - 1; i >= 0; i--) {
            while (amount >= coins[i]) {
                amount -= coins[i];
                count++;
            }
        }
        return amount == 0? count : -1;
    }
    public static void main(String[] args) {
        int[] coins = {25, 10, 5, 1};
        int amount = 63;
        int result = coinChange(coins, amount);
        System.out.println("最少需要 " + result + " 枚硬币");
    }
}

代码解析:先把硬币面值从大到小排序,然后从最大面值开始试,只要剩余金额够,就用这个面值的硬币,直到凑出目标金额。比如找 63 元,先拿 2 个 25 元(剩 13 元),再拿 1 个 10 元(剩 3 元),最后拿 3 个 1 元,总共 6 枚硬币,完美!

2. 活动选择问题:一天能参加多少活动?

你收到一堆活动邀请,但时间有冲突,每个活动都有开始和结束时间。贪心算法的思路是:优先选最早结束的活动,这样就能空出更多时间参加后续活动。

java 复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
class Activity {
    int start;
    int end;
    Activity(int start, int end) {
        this.start = start;
        this.end = end;
    }
}
public class ActivitySelection {
    public static List<Activity> selectActivities(Activity[] activities) {
        Arrays.sort(activities, Comparator.comparingInt(a -> a.end));
        List<Activity> selected = new ArrayList<>();
        selected.add(activities[0]);
        int lastEnd = activities[0].end;
        for (int i = 1; i < activities.length; i++) {
            if (activities[i].start >= lastEnd) {
                selected.add(activities[i]);
                lastEnd = activities[i].end;
            }
        }
        return selected;
    }
    public static void main(String[] args) {
        Activity[] activities = {
            new Activity(1, 4),
            new Activity(3, 5),
            new Activity(0, 6),
            new Activity(5, 7),
            new Activity(3, 8),
            new Activity(5, 9),
            new Activity(6, 10),
            new Activity(8, 11),
            new Activity(8, 12),
            new Activity(2, 13),
            new Activity(12, 14)
        };
        List<Activity> result = selectActivities(activities);
        System.out.println("最多能参加 " + result.size() + " 个活动");
    }
}

代码解析:先按活动结束时间排序,选第一个活动,然后从第二个活动开始,只要开始时间晚于上一个活动的结束时间,就加入活动列表。这样下来,你能参加的活动数量就是最多的!

3. 背包问题(部分背包):小偷的 "最优盗窃策略"

小偷闯入仓库,背包容量有限,但物品可以拆分(比如金条能切)。贪心算法会优先拿单位价值(价格 / 重量)最高的物品。

ini 复制代码
class Item {
    int value;
    int weight;
    Item(int value, int weight) {
        this.value = value;
        this.weight = weight;
    }
}
public class FractionalKnapsack {
    public static double fractionalKnapsack(int capacity, Item[] items) {
        Arrays.sort(items, (a, b) -> Double.compare((double)b.value / b.weight, (double)a.value / a.weight));
        double totalValue = 0;
        int currentWeight = 0;
        for (Item item : items) {
            if (currentWeight + item.weight <= capacity) {
                currentWeight += item.weight;
                totalValue += item.value;
            } else {
                int remaining = capacity - currentWeight;
                totalValue += (double)item.value / item.weight * remaining;
                break;
            }
        }
        return totalValue;
    }
    public static void main(String[] args) {
        Item[] items = {
            new Item(60, 10),
            new Item(100, 20),
            new Item(120, 30)
        };
        int capacity = 50;
        double result = fractionalKnapsack(capacity, items);
        System.out.println("背包能装的最大价值是 " + result);
    }
}

代码解析:先按单位价值给物品排序,优先装单位价值高的。装到背包快满时,拆分最后一个物品填满剩余空间,这样就能算出背包的最大价值。

贪心算法的 "翻车现场":这些坑千万别踩!

虽然贪心算法在上面这些场景很能打,但遇到下面这些问题,它就会瞬间 "露馅":

1. 0 - 1 背包问题:物品不能拆分的尴尬

还是小偷背包的例子,但这次物品不能拆分(比如整台电脑)。贪心算法可能先拿了价值高但很重的物品,导致后面装不下其他东西,结果总价值反而不是最优。比如背包容量 10,有 3 个物品:价值 60,重量 5、价值 100,重量 7、价值 120,重量 9,贪心算法会先选第二个物品,而最优解其实是选第一个和第三个。

2. 图的最短路径(复杂情况):只看眼前会迷路

在一个复杂的地图(图结构)里找最短路径,如果用贪心算法只选当前看起来最近的点,很可能走到死胡同,错过真正的最短路线。这时候就得用 Dijkstra 算法或弗洛伊德算法这类 "全局视野" 的方法。

3. 找零钱特殊货币:面值不按套路出牌

如果硬币面值是 1, 3, 4,想凑出 6 元,贪心算法会先拿 4 元,再拿两个 1 元,共 3 枚硬币。但最优解是两个 3 元,只需要 2 枚硬币。这就是因为贪心算法没考虑到整体最优。

总结:贪心算法的 "正确打开方式"

贪心算法就像一把双刃剑:

  • 优点:思路简单,代码好写,计算速度快,在某些问题上能快速得到最优解;
  • 缺点:太 "短视",容易陷入局部最优,面对复杂问题容易翻车。

想用贪心算法,记住这个口诀:先判断问题是否满足贪心选择性质和最优子结构。简单来说,就是每次局部最优选择不会影响最终全局最优。如果满足,大胆用它 "开挂";如果不确定,还是老老实实换其他算法吧!

相关推荐
m0_5873830038 分钟前
广州 24 小时自助健身房解决方案技术实现与实战指南
java·spring·小程序·架构·需求分析
·云扬·1 小时前
Prompt 工程与安全:从 System 设计到注入防御
java·人工智能·安全·ai·prompt
专注仿真1 小时前
引擎扩展性与集成设计深度剖析:从 14 个扩展点到 DIS→HLA 演进之路
开发语言·算法·仿真引擎·推演引擎
码爸1 小时前
支持向量机介绍与示例
算法·机器学习·支持向量机
怕浪猫1 小时前
2028编程圈很火的10个Skills
算法·面试·github
代码不停2 小时前
自动化测试(java)
java·开发语言·selenium·自动化
鬼手点金4 小时前
opencode-rules 插件
java·开发语言·前端·人工智能·chrome·git·github
paopaokaka_luck5 小时前
滑雪场管理系统(AI智能助手流式对话,ECharts 运营数据分析、教练、雪具与场地一体化预约,钱包支付退款、订单核销与服务评价)
java·人工智能·spring boot·网络协议·数据分析·echarts
Wang's Blog5 小时前
Java 项目实战: 交友项目 - Dubbo 的内部结构与一次调用的完整链路
java·dubbo
Wang's Blog5 小时前
Java 项目实战: 交友项目 - Dubbo 入门引导
java·dubbo·交友