Lemonade Change
Description
You are running a lemonade stand where each lemonade costs exactly **5, 20 bill.
You must provide the correct change to every customer so that each customer effectively pays $5 for their lemonade. You start with no bills in your cash register at all.
Given an integer array bills where bills[i] represents the bill the i-th customer pays with, return true if you can provide every customer with the correct change, or false otherwise.
Examples
Example 1
Input: bills = [5, 5, 5, 10, 20]
Output: true
Explanation: The first three customers each pay with a 5 bills. The fourth customer pays with 5 bill as change (we still have two 10 bill). The fifth customer pays with 15. We give back one 5 bill. Every customer received correct change, so the answer is true.
Example 2
Input: bills = [5, 5, 10, 10, 20]
Output: false
Explanation: The first two customers pay with 5 bills. The third customer pays with 5 bill as change (leaving us with one 10). The fourth customer also pays with 5 bill (leaving us with zero 10 bills). The fifth customer pays with 15 in change. We have two 5 bills. We cannot make 10 bills alone, so we return false.
Example 3
Input: bills = [5, 5, 10, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 20, 10, 5, 20]
Output: false
Explanation: This is a longer sequence that tests the greedy strategy. At some point in the sequence, a 15 in change.
Constraints
- 1 ≤ bills.length ≤ 10^5
- bills[i] is either 5, 10, or 20
Editorial
Brute Force
Intuition
The most straightforward way to think about this problem is to simulate the process exactly as described. Imagine you are the cashier standing behind the lemonade stand with a cash register in front of you.
Each time a customer walks up, they hand you a bill. If it is a 10 bill, you need to dig through your register and find a 20 bill, you need to find bills totaling $15 to return.
In this brute force simulation, we keep an explicit list of all the bills currently in our register. When we need to give change, we search through that list to find the right bills. For a 5 bill. For a 10 and a 5 bills.
This approach directly mirrors what a real cashier would do — rifling through the cash drawer looking for the right combination of bills.
Step-by-Step Explanation
Let's trace through bills = [5, 5, 10, 10, 20]:
Step 1: Customer 1 pays 5].
Step 2: Customer 2 pays 5, $5].
Step 3: Customer 3 pays 5 change. Search register for a 5, add 5, $10].
Step 4: Customer 4 pays 5 change. Search register for a 5, add 10, $10].
Step 5: Customer 5 pays 15 change. First try: search for a 5 — we have 5. Second try: search for three $5 bills — we have none. Cannot make change! Return false.
Brute Force — Simulating the Cash Register — Watch how we maintain an explicit list of bills and search through it each time we need to give change.
Algorithm
- Initialize an empty list to represent the cash register
- For each bill in the bills array:
- If the bill is 5 to register (no change needed)
- If the bill is 5 bill. If found, remove it and add $10. If not found, return false
- If the bill is 10 and a 5 bills. If both fail, return false
- If all customers served successfully, return true
Code
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
vector<int> reg; // cash register
for (int bill : bills) {
if (bill == 5) {
reg.push_back(5);
} else if (bill == 10) {
// Search for a $5 bill
auto it = find(reg.begin(), reg.end(), 5);
if (it == reg.end()) return false;
reg.erase(it);
reg.push_back(10);
} else {
// Try $10 + $5 first
auto it10 = find(reg.begin(), reg.end(), 10);
auto it5 = find(reg.begin(), reg.end(), 5);
if (it10 != reg.end() && it5 != reg.end()) {
reg.erase(it10);
it5 = find(reg.begin(), reg.end(), 5);
reg.erase(it5);
} else {
// Try three $5 bills
int count = 0;
for (auto it = reg.begin(); it != reg.end() && count < 3;) {
if (*it == 5) {
it = reg.erase(it);
count++;
} else {
++it;
}
}
if (count < 3) return false;
}
}
}
return true;
}
};class Solution:
def lemonadeChange(self, bills: list[int]) -> bool:
register = [] # list of bills in the cash register
for bill in bills:
if bill == 5:
register.append(5)
elif bill == 10:
if 5 in register:
register.remove(5)
register.append(10)
else:
return False
else: # bill == 20
# Try $10 + $5 first
if 10 in register and 5 in register:
register.remove(10)
register.remove(5)
elif register.count(5) >= 3:
for _ in range(3):
register.remove(5)
else:
return False
return Trueclass Solution {
public boolean lemonadeChange(int[] bills) {
List<Integer> register = new ArrayList<>();
for (int bill : bills) {
if (bill == 5) {
register.add(5);
} else if (bill == 10) {
if (register.contains(5)) {
register.remove(Integer.valueOf(5));
register.add(10);
} else {
return false;
}
} else {
// Try $10 + $5 first
if (register.contains(10) && register.contains(5)) {
register.remove(Integer.valueOf(10));
register.remove(Integer.valueOf(5));
} else {
int count = 0;
Iterator<Integer> it = register.iterator();
while (it.hasNext() && count < 3) {
if (it.next() == 5) {
it.remove();
count++;
}
}
if (count < 3) return false;
}
}
}
return true;
}
}Complexity Analysis
Time Complexity: O(n²)
For each of the n customers, we may need to search through the register list to find specific bills. The find or remove operation on a list takes O(n) time in the worst case (where n is the number of bills accumulated so far). Over all customers, this leads to O(n²) total work.
Space Complexity: O(n)
We store every received bill in the register list. In the worst case (all $5 payments), the list grows to size n.
Why This Approach Is Not Efficient
The brute force approach maintains an explicit list of bills and searches through it each time change is needed. This search takes O(n) per customer, leading to O(n²) total time.
With n up to 10^5, that means up to 10^10 operations in the worst case — far too slow for any reasonable time limit.
The core insight is that we do not need to track individual bills at all. Since there are only three denominations (10, 20 bills are never useful for making change, we only need to know how many 10 bills we have at any moment. Two simple integer counters replace the entire list, eliminating the need for searching.
Additionally, when giving change for a 10 + one 5 bills) is always optimal. This is because 10 and 10 bills can only be used for 5 bills gives us the best chance of serving future customers.
Optimal Approach - Greedy with Bill Counters
Intuition
Instead of keeping a list of every bill, we observe that only two numbers matter: how many 10 bills we have. We never need to track 15).
Think of it like a simplified cash register with just two compartments — one for 10 bills. When a customer pays:
- **5 compartment. Done.
- **5 compartment, drop the 10 compartment.
- **15. You have two options:
- Option A: One 5 bill = $15
- Option B: Three 15
The greedy choice is to always prefer Option A when available. Why? Because 5 needs at least one 10 bills are only useful when a 10 bill (which has limited use) instead of three $5 bills (which are highly flexible), we preserve our most valuable resource.
This greedy strategy is provably optimal: if a solution exists where you give three 20 while having a 10+5 bills for the remaining customers.
Step-by-Step Explanation
Let's trace through bills = [5, 5, 5, 10, 20]:
Step 1: Customer pays $5. No change needed. Increment fives counter.
- fives = 1, tens = 0
Step 2: Customer pays $5. No change needed. Increment fives counter.
- fives = 2, tens = 0
Step 3: Customer pays $5. No change needed. Increment fives counter.
- fives = 3, tens = 0
Step 4: Customer pays 5 change. We have fives = 3, so give one back.
- Decrement fives, increment tens.
- fives = 2, tens = 1
Step 5: Customer pays 15 change.
- Check Option A: do we have a 5? tens = 1 ≥ 1 and fives = 2 ≥ 1 — YES!
- Use one 5.
- fives = 1, tens = 0
Step 6: All customers served successfully. Return true.
Greedy Strategy — Tracking Bill Counters — Watch how two simple counters (fives and tens) replace an entire cash register. For $20 bills, we greedily prefer using a $10 + $5 to conserve our flexible $5 bills.
Algorithm
- Initialize two counters: fives = 0 and tens = 0
- For each bill in the bills array:
- If bill == 5: increment fives by 1
- If bill == 10: increment tens by 1, decrement fives by 1
- If bill == 20:
- If tens ≥ 1 (greedy preference): decrement tens by 1, decrement fives by 1
- Else: decrement fives by 3
- After processing, if fives < 0: return false (cannot make change)
- Return true (all customers served)
Code
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
int fives = 0, tens = 0;
for (int bill : bills) {
if (bill == 5) {
fives++;
} else if (bill == 10) {
tens++;
fives--;
} else { // bill == 20
if (tens > 0) {
tens--;
fives--;
} else {
fives -= 3;
}
}
if (fives < 0) return false;
}
return true;
}
};class Solution:
def lemonadeChange(self, bills: list[int]) -> bool:
fives = 0
tens = 0
for bill in bills:
if bill == 5:
fives += 1
elif bill == 10:
tens += 1
fives -= 1
else: # bill == 20
if tens > 0:
tens -= 1
fives -= 1
else:
fives -= 3
if fives < 0:
return False
return Trueclass Solution {
public boolean lemonadeChange(int[] bills) {
int fives = 0, tens = 0;
for (int bill : bills) {
if (bill == 5) {
fives++;
} else if (bill == 10) {
tens++;
fives--;
} else { // bill == 20
if (tens > 0) {
tens--;
fives--;
} else {
fives -= 3;
}
}
if (fives < 0) return false;
}
return true;
}
}Complexity Analysis
Time Complexity: O(n)
We iterate through the bills array exactly once. Each customer is processed in O(1) time — we only perform counter increments, decrements, and a single comparison. No searching through any collection is required. For n customers, the total work is O(n).
Space Complexity: O(1)
We use only two integer variables (fives and tens) regardless of the input size. No additional data structures are allocated, so space usage is constant.