Topics › Greedy Algorithms › Easy Problems
Lemonade Change
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
You run a lemonade stand where each lemonade costs $5. Customers stand in a queue and are served in order; customer i buys exactly one lemonade and pays with a single bill bills[i], which is 5, 10 or 20. You start with no money at all, and the only cash you can use for change is what earlier customers paid you. Return true if you can give every customer the correct change, otherwise false.
Constraints: 1 ≤ n ≤ 10^5, and every bills[i] is one of 5, 10, 20.
Example 1
bills = [5, 5, 5, 10, 20]
answer = true // keep three 5s, give a 5 back for the 10, give 10 + 5 back for the 20
Example 2
bills = [5, 5, 10, 10, 20]
answer = false // before the 20 arrives the drawer holds two 10s and no 5, so 15 cannot be made
What the drawer really needs to remember
Change is always bill - 5, so it is 0, 5 or 15. A $20 bill can never be part of any change (it is bigger than 15), so we never need to track how many 20s we hold. The whole state is just two counters: fives and tens.
- A
5needs no change:fives++. - A
10needs a5back. There is no choice: iffives == 0we fail. - A
20needs15back, and there are two ways to pay it:10 + 5or5 + 5 + 5.
The only decision in the whole problem is which of those two ways to use for a 20. That is what the approaches disagree on.
Approach 1: Brute force, try both ways for every 20
Intuition. If we do not know which way of making 15 is better, try both. Write a recursive function canServe(i, fives, tens) that answers “starting at customer i with this drawer, can everybody be served?”. For a 20 it tries 10 + 5 and, if that branch fails, 5 + 5 + 5.
Algorithm.
- If every customer has been served (
i == n), returntrue. - For a
5, recurse with one more five. - For a
10, fail if there is no five; otherwise recurse with one five fewer and one ten more. - For a
20, try each legal way of giving15and returntrueif any branch succeeds.
static boolean bruteForce(int[] bills) {
return canServe(bills, 0, 0, 0);
}
static boolean canServe(int[] bills, int i, int fives, int tens) {
if (i == bills.length) return true;
if (bills[i] == 5) return canServe(bills, i + 1, fives + 1, tens);
if (bills[i] == 10) return fives > 0 && canServe(bills, i + 1, fives - 1, tens + 1);
// a 20 needs 15 back: either 10 + 5 or 5 + 5 + 5
boolean ok = false;
if (tens > 0 && fives > 0) ok = canServe(bills, i + 1, fives - 1, tens - 1);
if (!ok && fives >= 3) ok = canServe(bills, i + 1, fives - 3, tens);
return ok;
}
Complexity. Time O(2^k) in the worst case, where k is the number of 20s, because each one can split the search in two. Space O(n) for the recursion stack, so a queue of 10^5 customers could overflow the default Java stack. This version is useful as a reference to test the greedy against, not as a submission.
Approach 2: Optimal, greedy counting
Intuition. Look at what the two options cost: 10 + 5 spends one ten and one five, 5 + 5 + 5 spends three fives. A 5 is the more flexible bill: it is needed for every 10 customer and can also be used for 20 customers, while a 10 is only ever useful as change for a 20. So whenever we have a ten available, spend it and keep the fives.
Why this never hurts: after 10 + 5 the drawer is (fives - 1, tens - 1); after 5 + 5 + 5 it is (fives - 3, tens). The first drawer has two extra fives and one fewer ten. Whatever the second drawer can do in the future, the first can do too: any time the second hands out that extra ten (always as part of 10 + 5 for a 20), the first can hand out two of its extra fives instead. So choosing 10 + 5 first is always at least as good, and the search tree from Approach 1 collapses to a single path.
Algorithm.
- Set
fives = 0,tens = 0. - For each bill:
5:fives++.10: iffives == 0returnfalse; elsefives--,tens++.20: iftens > 0 && fives > 0, spend one of each; else iffives >= 3, spend three fives; else returnfalse.
- Return
true.
static boolean optimal(int[] bills) {
int fives = 0, tens = 0;
for (int bill : bills) {
if (bill == 5) {
fives++;
} else if (bill == 10) {
if (fives == 0) return false;
fives--;
tens++;
} else { // bill == 20
if (tens > 0 && fives > 0) {
tens--;
fives--;
} else if (fives >= 3) {
fives -= 3;
} else {
return false;
}
}
}
return true;
}
Dry run on bills = [5, 5, 5, 5, 10, 20, 10, 10], a queue built to punish the wrong preference:
| bill | change given | fives | tens |
|---|---|---|---|
| 5 | none | 1 | 0 |
| 5 | none | 2 | 0 |
| 5 | none | 3 | 0 |
| 5 | none | 4 | 0 |
| 10 | 5 | 3 | 1 |
| 20 | 10 + 5 | 2 | 0 |
| 10 | 5 | 1 | 1 |
| 10 | 5 | 0 | 2 |
Every customer is served, so the answer is true. Had we paid the 20 with three fives, the drawer would have been (1, 1), the next 10 would leave (0, 2), and the last 10 would find no five to give back.
Complexity. Time O(n): one pass, constant work per customer. Space O(1): two counters.
Edge cases and pitfalls
- First customer pays with 10 or 20. The drawer is empty, so the answer is
falseimmediately.[10]and[20]are both tests. - Empty queue. Nobody needs change, so the answer is
true. - Wrong preference for 20s. Trying
5 + 5 + 5before10 + 5passes the easy examples and fails cases like the dry run above. This is the classic bug in this problem. - Tracking 20s. Counting twenties is harmless but useless; they can never be handed out as change.
- Order matters. The same multiset of bills can succeed or fail depending on order (
[5, 10]works,[10, 5]does not), so do not sort the input.
Related problems
- Assign Cookies is another short greedy problem where you spend the least useful resource first.
- Minimum coins asks for the fewest coins to make an amount, where greedy works only for canonical coin systems.
- Fractional Knapsack shows a greedy choice justified by an exchange argument.