贪心算法

# 贪心算法

# 📋 学习目标

  • 理解贪心算法的基本思想
  • 掌握贪心选择性质和最优子结构
  • 学会证明贪心算法的正确性
  • 熟练解决经典贪心问题

# 🎯 算法思想

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

# 核心特征

  1. 贪心选择性质:局部最优选择能导致全局最优解
  2. 最优子结构:问题的最优解包含子问题的最优解
  3. 无后效性:某阶段状态一旦确定,就不受这个状态以后决策的影响

# 🏆 经典问题

# 1. 活动选择问题

问题描述:给定n个活动,每个活动都有开始时间和结束时间,选择最多的活动使得它们互不冲突。

public int activitySelection(int[] start, int[] end) {
    int n = start.length;
    // 按结束时间排序
    Integer[] indices = new Integer[n];
    for (int i = 0; i < n; i++) {
        indices[i] = i;
    }
    Arrays.sort(indices, (a, b) -> end[a] - end[b]);
  
    int count = 1;
    int lastEnd = end[indices[0]];
  
    for (int i = 1; i < n; i++) {
        if (start[indices[i]] >= lastEnd) {
            count++;
            lastEnd = end[indices[i]];
        }
    }
  
    return count;
}

# 2. 分发饼干

问题描述:假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

public int findContentChildren(int[] g, int[] s) {
    Arrays.sort(g); // 孩子的胃口
    Arrays.sort(s); // 饼干的尺寸
  
    int child = 0, cookie = 0;
    while (child < g.length && cookie < s.length) {
        if (g[child] <= s[cookie]) {
            child++;
        }
        cookie++;
    }
  
    return child;
}

# 3. 跳跃游戏

问题描述:给定一个非负整数数组,你最初位于数组的第一个位置,数组中的每个元素代表你在该位置可以跳跃的最大长度。

public boolean canJump(int[] nums) {
    int maxReach = 0;
    for (int i = 0; i < nums.length; i++) {
        if (i > maxReach) {
            return false;
        }
        maxReach = Math.max(maxReach, i + nums[i]);
    }
    return true;
}

# 💡 解题技巧

  1. 排序:很多贪心问题需要先排序
  2. 局部最优:每一步都选择当前最优解
  3. 证明正确性:需要证明局部最优能导致全局最优
  4. 反证法:常用于证明贪心算法的正确性

# 🔍 练习题目

# 初级题目

# 中级题目

# 高级题目