860. 柠檬水找零(Lemonade Change)

频次 ★★★ · 难度 🟢 · 高频:字节

题目

柠檬水摊位,每杯 5 元。顾客排队购买,每人只买一杯,支付 5 元、10 元或 20 元。初始无零钱,判断能否正确找零。

示例

输入: bills = [5,5,5,10,20]
输出: true
解释:前 3 位收 5 元;第 4 位收 10 元找 5 元;第 5 位收 20 元找 10+5 元

输入: bills = [5,5,10,10,20]
输出: false
解释:前 4 位后剩 2 张 10 元;第 5 位收 20 元,无法找零(没有 5 元)

思路

贪心(优先用 10 元找零):维护 5 元和 10 元的数量。遇到 20 元需要找零 15 元时,优先用 10+5 的组合(保留 5 元的灵活性),只有没有 10 元时才用 3 张 5 元。因为 5 元是万能的(可以找 10 元和 20 元),10 元只能找 20 元。

代码

public boolean lemonadeChange(int[] bills) {
    int five = 0, ten = 0;
    for (int bill : bills) {
        if (bill == 5) {
            five++;
        } else if (bill == 10) {
            if (five == 0) return false;
            five--;
            ten++;
        } else { // bill == 20
            // 贪心:优先用 10+5 找零,保留 5 元的灵活性
            if (ten > 0 && five > 0) {
                ten--;
                five--;
            } else if (five >= 3) {
                five -= 3;
            } else {
                return false;
            }
        }
    }
    return true;
}

复杂度

  • 时间:O(n) —— 一次遍历
  • 空间:O(1)

边界条件

  • 第一个人不付 5 元:立即返回 false(无零钱找)
  • 全是 5 元:永远正确,返回 true
  • 只有 5 元和 10 元:只需检查 5 元数量是否够
  • 连续多个 20 元:快速消耗零钱,容易失败

变式

  • 455. 分发饼干:同为贪心入门,但关注”分配”而非”找零”
  • 322. 零钱兑换:找零的 DP 版本(面额不限 + 最少硬币数),本题是贪心特例

易错点

  • 贪心策略的具体体现:20 元找零时优先 10+5 而非 3×5。这是唯一需要贪心决策的地方
  • 不需要维护 20 元数量:20 元只能用于找零更大的面额,但本题最大面额就是 20,所以 20 元对后续找零无帮助,无需统计
  • 5 元是最关键的资源:任何时候 5 元不够就失败,因为 5 元是找 10 元和 20 元的必要组成部分

面试追问

  • 为什么贪心是对的? 面对 20 元时,10+5 和 3×5 都能找零。但 10 元只能找 20 元,而 5 元既能找 10 元也能找 20 元,所以优先花掉 10 元(保留 5 元)是局部最优,进而全局最优
  • 如果顾客可以支付 50 元? 需要更复杂的贪心策略,通常用 DP 或回溯

关联题