零基础数据结构与算法——第五章:高级算法-贪心算法-基础&示例

5.2 贪心算法(Greedy Algorithm)

5.2.1 贪心算法的基本概念

什么是贪心算法?

贪心算法是一种在每一步选择中都采取当前状态下最好或最优的选择,从而希望导致结果是最好或最优的算法。

生活例子

想象你在超市购物,手里有100元钱,想买尽可能多的零食。如果你采用贪心策略,你会怎么做?你可能会先选择最便宜的零食,然后是第二便宜的,以此类推,直到钱用完。这就是一种贪心策略------每次都选择当前看起来最优(最便宜)的选择。

核心思想

贪心算法的核心思想是:每一步都选择当前看起来最优的解,而不考虑后续的影响。

图解

复制代码
问题:从A点到F点,找一条总权重最小的路径

    B(2)---D(3)
   /|      /|
  / |     / |
 A  |    /  |
  \ |   /   |
   \|  /    |
    C(1)    E(1)---F

贪心策略:每次选择当前权重最小的边

  1. 从A出发,选择权重最小的边:A→C(1)
  2. 从C出发,选择权重最小的边:C→E(1)
  3. 从E出发,选择权重最小的边:E→F(1)

最终路径:A→C→E→F,总权重:3

适用条件

贪心算法要求问题具有两个性质:

  1. 最优子结构:问题的最优解包含子问题的最优解。

    生活例子:如果从北京到上海的最短路径经过南京,那么北京到南京的这段路径一定是北京到南京的最短路径。

  2. 贪心选择性质:通过一系列局部最优的选择可以得到全局最优解。

    生活例子:在找零钱问题中(假设有面值为1, 5, 10, 20, 50, 100的硬币),如果要找86元,最少需要多少硬币?贪心策略是每次都选择不超过剩余金额的最大面值:先选100(不行),再选50,再选20,再选10,再选5,最后选1(1枚)。结果是:50 + 20 + 10 + 5 + 1 = 86,共5枚硬币。这个贪心策略在我们的货币系统下能得到最优解。

贪心算法与动态规划的区别
特点 贪心算法 动态规划
决策方式 每步做出局部最优选择 考虑所有可能的选择
子问题 不重新考虑以前的选择 保存并重用子问题的解
适用性 问题满足贪心选择性质 问题具有重叠子问题
效率 通常更高效 通常需要更多时间和空间
最优性 不一定得到全局最优解 保证得到全局最优解

生活例子对比

假设你要爬一座有多条路径的山,目标是到达山顶:

  • 贪心算法:每次都选择当前最陡的路径前进,希望能最快到达山顶。这可能会导致你走入死胡同或者不是最短路径。
  • 动态规划:你会考察所有可能的路径,记录每条路径的长度,然后选择最短的那条。
贪心算法的优缺点

优点

  • 简单直观,容易实现
  • 时间复杂度通常较低
  • 不需要考虑所有可能的解

缺点

  • 不是所有问题都适用
  • 可能无法得到全局最优解
  • 需要证明贪心策略的正确性
如何设计贪心算法
  1. 确定问题是否适合贪心:检查问题是否具有最优子结构和贪心选择性质。
  2. 设计贪心策略:确定每一步的选择标准。
  3. 证明正确性:证明通过贪心策略能得到全局最优解。
  4. 实现算法:编写代码实现贪心策略。

注意:贪心算法的正确性证明通常是最困难的部分,有时需要使用数学归纳法或反证法。

5.2.2 经典贪心算法问题

活动选择问题

问题描述:有n个活动,每个活动有开始时间和结束时间。要求选择尽可能多的活动,使得所选活动互不重叠。

贪心解法:按照活动的结束时间排序,每次选择结束时间最早的活动。

java 复制代码
public static int maxActivities(int[] start, int[] finish) {
    int n = start.length;
    
    // 创建活动数组并按结束时间排序
    Activity[] activities = new Activity[n];
    for (int i = 0; i < n; i++) {
        activities[i] = new Activity(start[i], finish[i]);
    }
    Arrays.sort(activities, (a, b) -> a.finish - b.finish);
    
    int count = 1; // 至少选择一个活动
    int lastFinish = activities[0].finish;
    
    for (int i = 1; i < n; i++) {
        if (activities[i].start >= lastFinish) {
            count++;
            lastFinish = activities[i].finish;
        }
    }
    
    return count;
}

static class Activity {
    int start, finish;
    
    public Activity(int start, int finish) {
        this.start = start;
        this.finish = finish;
    }
}
相关推荐
Tisfy15 分钟前
LeetCode 3202.找出有效子序列的最大长度 II:取模性质(动态规划)
算法·leetcode·动态规划·题解·模运算
Tiny番茄42 分钟前
LeetCode 121. 买卖股票的最佳时机
算法·leetcode·职场和发展
乔宕一1 小时前
定点小数与分数
算法
不知名。。。。。。。。1 小时前
分治算法---归并
算法
阿群今天学习了吗2 小时前
面向对象基础笔记
笔记·学习·算法
☞下凡☜3 小时前
C语言(20250717)
linux·c语言·算法
lemon_sjdk3 小时前
LWJGL教程(2)——游戏循环
java·人工智能·算法·游戏
盒子69104 小时前
leetcode丑数II计算第n个丑数
算法·leetcode·职场和发展
小蛋编程4 小时前
【图论】图的定义与一些常用术语
c++·算法
拾光拾趣录4 小时前
两数相除算法
前端·算法