178 lines
8.4 KiB
YAML
178 lines
8.4 KiB
YAML
title: Lemonade Change
|
|
slug: lemonade-change
|
|
difficulty: easy
|
|
leetcode_id: 860
|
|
leetcode_url: https://leetcode.com/problems/lemonade-change/
|
|
categories:
|
|
- arrays
|
|
patterns:
|
|
- greedy
|
|
|
|
description: |
|
|
At a lemonade stand, each lemonade costs `$5`. Customers are standing in a queue to buy from you and order one at a time (in the order specified by `bills`). Each customer will only buy one lemonade and pay with either a `$5`, `$10`, or `$20` bill. You must provide the correct change to each customer so that the net transaction is that the customer pays `$5`.
|
|
|
|
Note that you do not have any change in hand at first.
|
|
|
|
Given an integer array `bills` where `bills[i]` is the bill the i<sup>th</sup> customer pays, return `true` *if you can provide every customer with the correct change*, or `false` *otherwise*.
|
|
|
|
constraints: |
|
|
- `1 <= bills.length <= 10^5`
|
|
- `bills[i]` is either `5`, `10`, or `20`
|
|
|
|
examples:
|
|
- input: "bills = [5,5,5,10,20]"
|
|
output: "true"
|
|
explanation: "From the first 3 customers, we collect three $5 bills. From the fourth customer, we collect a $10 bill and give back a $5. From the fifth customer, we give a $10 bill and a $5 bill. Since all customers got correct change, we output true."
|
|
- input: "bills = [5,5,10,10,20]"
|
|
output: "false"
|
|
explanation: "From the first two customers, we collect two $5 bills. For the next two customers, we collect a $10 bill and give back a $5 bill each. For the last customer, we cannot give the change of $15 back because we only have two $10 bills (no $5 bills left)."
|
|
|
|
explanation:
|
|
intuition: |
|
|
Imagine you're actually running a lemonade stand with a cash register. You start with an empty register and customers line up to pay.
|
|
|
|
The key insight is that **$5 bills are the most valuable** — not because of their face value, but because they're the most *versatile* for making change. A $5 bill can be used to:
|
|
- Give change for a $10 bill (need one $5)
|
|
- Give change for a $20 bill (need one $10 + one $5, OR three $5s)
|
|
|
|
Think of it like this: when a customer pays with $20, you have two options for the $15 change:
|
|
1. One $10 + one $5 (preferred — uses the less versatile $10)
|
|
2. Three $5 bills (backup — depletes your precious $5s)
|
|
|
|
The greedy choice is always to **preserve your $5 bills** when possible. Use $10 bills first when giving change for $20, because $10 bills can only be used for one purpose (change for $20), while $5 bills can be used for both $10 and $20 transactions.
|
|
|
|
approach: |
|
|
We solve this using a **Greedy Simulation**:
|
|
|
|
**Step 1: Initialise counters**
|
|
|
|
- `fives`: Count of $5 bills in hand, starts at `0`
|
|
- `tens`: Count of $10 bills in hand, starts at `0`
|
|
- We don't need to track $20 bills — they can never be used as change
|
|
|
|
|
|
|
|
**Step 2: Process each customer in order**
|
|
|
|
- **If customer pays $5**: No change needed. Increment `fives` by 1
|
|
- **If customer pays $10**: Need to give $5 change. If `fives == 0`, return `false`. Otherwise, decrement `fives`, increment `tens`
|
|
- **If customer pays $20**: Need to give $15 change. Try the greedy choice first:
|
|
- If we have at least one $10 and one $5: use them (decrement both)
|
|
- Else if we have at least three $5s: use them (decrement `fives` by 3)
|
|
- Else: return `false` — we can't make change
|
|
|
|
|
|
|
|
**Step 3: Return the result**
|
|
|
|
- If we process all customers successfully, return `true`
|
|
|
|
|
|
|
|
The greedy strategy works because using a $10 bill when available always leaves us in a better (or equal) position than using three $5 bills.
|
|
|
|
common_pitfalls:
|
|
- title: Not Prioritising $10 Bills for $20 Change
|
|
description: |
|
|
When giving change for $20, some might randomly choose between using three $5s or one $10 + one $5. This can lead to failure.
|
|
|
|
For example, with `bills = [5,5,10,20,5,5,5,10,20,20]`:
|
|
- If you use three $5s for the first $20 instead of $10 + $5, you might run out of $5 bills later when a $10 customer arrives
|
|
|
|
Always prefer using $10 bills for $20 change — $5 bills are more versatile.
|
|
wrong_approach: "Random or first-available bill selection"
|
|
correct_approach: "Greedy: prefer $10 + $5 over three $5s"
|
|
|
|
- title: Tracking $20 Bills
|
|
description: |
|
|
It's tempting to track all bill types, but $20 bills are useless for making change. You can never give a $20 bill back to a customer (since the most change needed is $15).
|
|
|
|
Tracking $20 bills wastes space and adds unnecessary complexity.
|
|
wrong_approach: "Maintaining a counter for $20 bills"
|
|
correct_approach: "Only track $5 and $10 bills"
|
|
|
|
- title: Forgetting the Empty Register Start
|
|
description: |
|
|
The problem states you start with no change. If the first customer pays with anything other than $5, you immediately fail.
|
|
|
|
For input `bills = [10,5,5,5,5]`, the answer is `false` because you can't give change to the very first customer.
|
|
wrong_approach: "Assuming some initial change is available"
|
|
correct_approach: "Start with fives = 0 and tens = 0"
|
|
|
|
key_takeaways:
|
|
- "**Greedy pattern**: When multiple valid choices exist, prefer the one that keeps more options open (preserve $5 bills)"
|
|
- "**Simulation**: Sometimes the best approach is to simulate the process step by step"
|
|
- "**Track only what matters**: $20 bills are never used as change, so don't track them"
|
|
- "**Order matters**: The greedy choice ($10 + $5 over three $5s) ensures we handle future customers optimally"
|
|
|
|
time_complexity: "O(n). We process each customer exactly once with O(1) operations per customer."
|
|
space_complexity: "O(1). We only use two integer counters (`fives` and `tens`) regardless of input size."
|
|
|
|
solutions:
|
|
- approach_name: Greedy Simulation
|
|
is_optimal: true
|
|
code: |
|
|
def lemonade_change(bills: list[int]) -> bool:
|
|
# Track only $5 and $10 bills (we never use $20 for change)
|
|
fives = 0
|
|
tens = 0
|
|
|
|
for bill in bills:
|
|
if bill == 5:
|
|
# No change needed, just collect the $5
|
|
fives += 1
|
|
|
|
elif bill == 10:
|
|
# Need to give $5 change
|
|
if fives == 0:
|
|
return False # Can't make change
|
|
fives -= 1
|
|
tens += 1
|
|
|
|
else: # bill == 20
|
|
# Need to give $15 change
|
|
# Greedy: prefer using $10 + $5 to preserve $5 bills
|
|
if tens > 0 and fives > 0:
|
|
tens -= 1
|
|
fives -= 1
|
|
elif fives >= 3:
|
|
fives -= 3
|
|
else:
|
|
return False # Can't make change
|
|
|
|
return True # Successfully served all customers
|
|
explanation: |
|
|
**Time Complexity:** O(n) — Single pass through the bills array.
|
|
|
|
**Space Complexity:** O(1) — Only two counters used.
|
|
|
|
We simulate serving each customer, making the greedy choice to preserve $5 bills when possible. The key insight is that $10 + $5 is always preferred over three $5s for $20 change, because $5 bills are needed for both $10 and $20 transactions while $10 bills are only useful for $20 transactions.
|
|
|
|
- approach_name: Brute Force Simulation
|
|
is_optimal: false
|
|
code: |
|
|
def lemonade_change(bills: list[int]) -> bool:
|
|
# Track all bills (including $20, though unnecessary)
|
|
cash = {5: 0, 10: 0, 20: 0}
|
|
|
|
for bill in bills:
|
|
cash[bill] += 1 # Receive payment
|
|
change_needed = bill - 5
|
|
|
|
# Try to make change using largest bills first
|
|
for denomination in [20, 10, 5]:
|
|
while change_needed >= denomination and cash[denomination] > 0:
|
|
change_needed -= denomination
|
|
cash[denomination] -= 1
|
|
|
|
if change_needed > 0:
|
|
return False # Couldn't make exact change
|
|
|
|
return True
|
|
explanation: |
|
|
**Time Complexity:** O(n) — Still linear, but with more operations per customer.
|
|
|
|
**Space Complexity:** O(1) — Fixed-size dictionary.
|
|
|
|
This approach uses a general change-making algorithm: try to use the largest bills first. While it works, it's unnecessarily complex for this specific problem. It also tracks $20 bills (which are never used) and uses a loop where direct conditionals suffice. The greedy simulation above is cleaner and more efficient in practice.
|