当前位置: 首页 > article >正文

LeetCode 2517礼盒的最大甜蜜度

🍬 礼盒的最大甜蜜度:二分查找 + 贪心算法详解

📌 题目描述

给定正整数数组 price(第 i 类糖果价格)和正整数 k,商店将 k 类不同糖果打包成礼盒。礼盒的甜蜜度是礼盒中任意两种糖果价格绝对差的最小值。求礼盒的最大甜蜜度

💡 示例:
输入:price = [1,3,5,7], k = 3
输出:2
解释:选 [1,3,5] 或 [3,5,7],最小差均为 2,是最大可能值。

🧠 核心思路:最大化最小值问题

这类问题的典型解法是 二分查找(Binary Search),核心步骤:

  1. 排序数组:方便计算元素间的差值。
  2. 二分甜蜜度:在可能的甜蜜度范围内(0 到最大差)搜索最大值。
  3. 贪心验证可行性:对每个中间值,检查是否能选出 k 个数满足最小差≥当前值。

🔍 算法步骤详解

1. 排序数组

Arrays.sort(price); // 升序排列,如[1,3,5,7]
  • 排序后,元素间的差值更容易处理,且贪心选择时只需考虑相邻元素。

2. 二分查找甜蜜度

int left = 0, right = price[price.length-1] - price[0];
while (left < right) {
    int mid = (left + right + 1) / 2; // 向上取整避免死循环
    if (canChoose(price, k, mid)) {
        left = mid; // 可行,尝试更大的甜蜜度
    } else {
        right = mid - 1; // 不可行,尝试更小的甜蜜度
    }
}
return left; // 最终left==right,即最大可行甜蜜度
  • 搜索范围[0, 最大差](如示例中 0 到 6)。
  • 关键点mid=(left+right+1)/2 向上取整,确保每次迭代至少缩小范围(避免left=1, right=2时陷入死循环)。

3. 贪心验证可行性(核心函数)

private boolean canChoose(int[] price, int k, int tastiness) {
    int count = 1; // 初始选第一个元素
    int prev = price[0]; // 记录上一个选中的元素
    for (int i = 1; i < price.length; i++) {
        if (price[i] - prev >= tastiness) { // 当前元素与上一个的差≥tastiness
            count++; // 选中当前元素
            prev = price[i]; // 更新上一个元素
            if (count >= k) return true; // 提前返回
        }
    }
    return count >= k; // 最终是否满足数量
}
  • 贪心策略:每次选离当前元素最近的满足条件的元素,确保选出最多的元素。
  • 原理:排序后,相邻选中元素的差≥tastiness → 所有选中元素的差的最小值≥tastiness。

🚀 完整代码(Java)

import java.util.Arrays;

class Solution {
    public int maximumTastiness(int[] price, int k) {
        Arrays.sort(price); // 排序是关键第一步
        int left = 0, right = price[price.length - 1] - price[0];
        while (left < right) {
            int mid = (left + right + 1) / 2; // 向上取整
            if (canChoose(price, k, mid)) {
                left = mid; // 可行,搜索右半部分
            } else {
                right = mid - 1; // 不可行,搜索左半部分
            }
        }
        return left;
    }

    private boolean canChoose(int[] price, int k, int tastiness) {
        int count = 1; // 至少选1个(第一个元素)
        int prev = price[0];
        for (int num : price) { // 遍历每个元素(从第二个开始)
            if (num - prev >= tastiness) { // 满足最小差条件
                count++;
                prev = num; // 更新上一个选中的元素
                if (count >= k) return true; // 提前终止
            }
        }
        return count >= k; // 检查是否选够k个
    }
}

📊 复杂度分析

操作时间复杂度说明
排序O(n log n)快速排序的平均时间复杂度
二分查找O(log m)m 是价格最大值与最小值的差
每次贪心验证O(n)遍历数组一次
总时间复杂度O(n log n)排序是主导操作
空间复杂度O(log n)排序的栈空间(取决于语言实现)

🌰 示例解析(price=[1,3,5,7], k=3)

  1. 排序后:[1,3,5,7]
  2. 二分过程
    • 初始:left=0, right=6(7-1=6)
    • mid=3((0+6+1)/2=3.5→3):
      • 验证:选 1→下一个≥4(1+3)→5(差 4≥3),count=2;下一个≥8→无。count=2<3→不可行→right=2。
    • mid=1((0+2+1)/2=1.5→1):
      • 验证:选 1→3(差 2≥1),count=2;5(差 2≥1),count=3≥3→可行→left=1。
    • mid=2((1+2+1)/2=2):
      • 验证:选 1→3(差 2),count=2;5(差 2),count=3≥3→可行→left=2。
    • 结束:left==right=2→返回 2(正确)。

💡 关键细节与边界处理

  1. k=1 的情况:任意选 1 个元素,甜蜜度为 0(无差值)。
    • 代码自动处理:canChoose初始 count=1,直接返回 true。
  2. 所有元素相同:甜蜜度为 0(差均为 0)。
  3. 向上取整的原因:避免死循环(如 left=1, right=2 时,mid=(1+2)/2=1→进入死循环)。

🎯 同类问题扩展

  • 经典问题:《放置花朵最大化最小距离》《分割数组的最大值》。
  • 通用解法:最大化最小值问题 → 二分查找 + 贪心 / 二分答案。

📝 总结

  1. 排序:为贪心选择提供有序基础。
  2. 二分查找:在可能的甜蜜度范围内高效搜索最大值。
  3. 贪心验证:用 “每次选最近满足条件的元素” 策略,快速判断可行性。

这种方法的时间复杂度低(主导为排序的 O (n log n)),空间复杂度友好,是解决 “最大化最小值” 问题的标准解法。通过本题,读者可以掌握二分查找与贪心算法的结合技巧,举一反三解决类似问题。

✨ 代码优化点:

 
  • 贪心函数中使用增强 for 循环(for (int num : price))提升可读性。
  • 提前终止循环(if (count >= k) return true)减少不必要的遍历。

如果有任何疑问或想进一步探讨,欢迎在评论区留言! 😊


http://www.kler.cn/a/596113.html

相关文章:

  • 嵌入式面经(2)——央企篇
  • 嵌入式C语言进阶(四)查漏补缺
  • MATLAB 实现 Chatterjee 相关系数矩阵计算与特征选择
  • 银联无感支付实现
  • 对接豆包大模型
  • 机器学习——分类、回归、聚类、LASSO回归、Ridge回归(自用)
  • 深入理解 Redis SDS:高效字符串存储的秘密
  • PostgreSQL 常用函数 覆盖九成场景
  • GitHub Copilot Extensions:解锁开发者新能力
  • 人工智能之数学基础:线性方程组求解的得力助手——增广矩阵
  • Kafka消息自定义序列化
  • golang接口用法-代码案例
  • vulhub靶场matrix-breakout-2-morpheus
  • java设计模式之工厂模式《铸剑风云录》
  • vue3:ref , reactive
  • 论华为 Pura X 折叠屏性能检测
  • S32K144外设实验(三):ADC单通道连续采样(中断)
  • AudioTrack
  • 树莓集团数字产业布局解读:战略+商业双驱动
  • 【数据挖掘】Python基础环境安装配置