Number of Students Unable to Eat Lunch: Queue Simulation
You are given two integer arrays. students[i] is the sandwich preference of the i-th student (0 for circular, 1 for square), and sandwiches[i] is the type of the i-th sandwich in a stack (top at index 0). Students stand in a queue. The front student takes the top sandwich if it matches their preference. Otherwise, they go to the back of the queue. This continues until no student in the queue wants the top sandwich. Return the number of students who cannot eat.
This is LeetCode 1700: Number of Students Unable to Eat Lunch, an easy problem that looks like it needs queue simulation but actually reduces to a counting problem. The trick is realizing that the queue order does not matter.
Why this problem matters
At first glance, this problem seems to require simulating the queue rotation: move the front student to the back, check again, repeat. Many people write exactly that simulation and it works. But the simulation hides the real insight.
This problem teaches you to look past the mechanics of a data structure and ask: "What actually determines the outcome?" The answer is not the queue order. It is whether any student in the queue wants the top sandwich. That shift in thinking, from simulation to counting, is a pattern you will use again and again in interview problems.
The key insight
The queue order does not matter. Here is why: if the front student does not want the top sandwich, they go to the back. The next student gets a chance. Then the next. Eventually, every student who wants the top sandwich will cycle to the front and take it. The only thing that stops the process is when no remaining student wants the current top sandwich.
So instead of simulating the queue, you can count how many students prefer type 0 and how many prefer type 1. Then process the sandwich stack from top to bottom. For each sandwich, check if any student still wants it. If yes, decrement that count. If no, everyone remaining is stuck and cannot eat.
The solution
def countStudents(students, sandwiches):
count = [0, 0]
for s in students:
count[s] += 1
for sandwich in sandwiches:
if count[sandwich] == 0:
return count[0] + count[1]
count[sandwich] -= 1
return 0
Here is what each piece does:
-
count = [0, 0]creates a two-element array wherecount[0]tracks how many students want type 0 andcount[1]tracks how many want type 1. -
The first loop counts student preferences. After this loop, you know exactly how many students want each sandwich type.
-
The second loop processes sandwiches from the top of the stack. For each sandwich, if
count[sandwich]is 0, no remaining student wants it. The queue would spin forever. Return the total number of remaining students (count[0] + count[1]). -
If
count[sandwich]is positive, at least one student wants this sandwich. They will eventually reach the front and take it. Decrement the count. -
If you process every sandwich without returning early, all students ate. Return 0.
When a problem involves a queue where items cycle back to the end, ask yourself: does the order actually matter? If every item gets a chance eventually, you can often replace simulation with counting. This transforms an O(n^2) simulation into an O(n) pass.
Visual walkthrough
Let's trace through students = [1,1,0,0] and sandwiches = [0,1,0,1]. We count preferences first (two 0s, two 1s), then process sandwiches from the top.
Initial state: Count student preferences.
2 students want type 0, 2 students want type 1. Process sandwiches from the top.
Step 1: Top sandwich is 0. A student wants it.
count0 = 2, so at least one student wants this sandwich. They take it. count0 drops to 1.
Step 2: Top sandwich is 1. A student wants it.
count1 = 2, so a student takes this sandwich. count1 drops to 1.
Step 3: Top sandwich is 0. A student wants it.
count0 = 1, so the last type-0 student takes it. count0 drops to 0.
Step 4: Top sandwich is 1. A student wants it.
count1 = 1, so the last type-1 student takes it. count1 drops to 0.
Done. All sandwiches taken. Answer: 0.
No sandwiches left, no students left. Everyone ate. Return 0.
Every sandwich found a willing student. All four students ate. The answer is 0.
Now consider what happens with students = [1,1,1,0,0,1] and sandwiches = [1,0,0,0,1,1]. After counting: three students want type 1 and two want type 0. The first sandwich is 1 (count1 drops to 2). The second is 0 (count0 drops to 1). The third is 0 (count0 drops to 0). The fourth is 0, but count0 is already 0. No one wants it. Three students remain (0 + 3). Return 3.
Complexity analysis
| Metric | Value |
|---|---|
| Time | O(n), two passes through the arrays |
| Space | O(1), only a two-element counter array |
This is optimal. You must read every student preference and every sandwich at least once, so O(n) is the lower bound. The counter array is fixed size regardless of input.
The building blocks
This problem is built on two reusable patterns that CodeBricks drills independently.
1. Counting to replace simulation
The pattern of counting occurrences to avoid simulating a process step by step:
count = [0] * k
for item in sequence:
count[item] += 1
Instead of moving elements around in a queue and watching what happens, you precompute the frequencies and reason about whether each step is possible. You see the same idea in problems like "First Unique Character in a String" (count character frequencies, then scan), "Majority Element" (count to determine the winner), and "Task Scheduler" (count tasks to compute idle slots). The core insight is always the same: counting lets you skip the simulation.
2. Early termination on impossibility
The pattern of checking a feasibility condition at each step and returning immediately when it fails:
for item in sequence:
if cannot_proceed(item):
return remaining_count
update_state(item)
return 0
In this problem, impossibility means no student wants the current sandwich. In "Gas Station," it means the tank goes negative. In "Lemonade Change," it means you cannot make change. The skeleton is the same: process items one at a time and bail out as soon as continuing becomes impossible.
Edge cases
Before submitting, make sure your solution handles these:
- All students want the same type
students = [0,0,0], sandwiches = [0,0,0]: everyone eats. Returns 0. - First sandwich has no takers
students = [0,0,0], sandwiches = [1,0,0]: no student wants type 1. All 3 are stuck immediately. Returns 3. - Single student
students = [1], sandwiches = [1]: they take it. Returns 0. - Single student, wrong type
students = [0], sandwiches = [1]: they do not want it. Returns 1. - Mismatch partway through
students = [1,1,0], sandwiches = [0,1,1]: count0=1, count1=2. First sandwich is 0, count0 drops to 0. Second is 1, count1 drops to 1. Third is 1, count1 drops to 0. Returns 0. - All stuck at the end
students = [0,0], sandwiches = [1,1]: count0=2, count1=0. First sandwich is 1, but count1=0. Returns 2.
The counting solution handles all of these without special-case logic.
From understanding to recall
You have seen the counting solution and it makes sense. Count preferences, process sandwiches, bail when no one wants the top one. Clean. But can you write it from scratch in an interview without looking at it?
The details matter: using a two-element array for counts, checking count[sandwich] == 0 before decrementing, and returning count[0] + count[1] instead of just one count. These are small but critical, and they are easy to get wrong under pressure if you have not practiced writing them from memory.
Spaced repetition closes that gap. You practice writing the counting loop and the early termination check from scratch at increasing intervals. After a few rounds, the pattern is automatic. You see "queue cycling until stuck" in a problem description, and the counting approach flows out without hesitation.
The "counting to replace simulation" pattern is one of roughly 60 reusable building blocks that cover hundreds of LeetCode problems. Learning them individually and drilling them with spaced repetition is far more effective than grinding random problems and hoping they stick.
Related posts
- Implement Queue Using Stacks - Building a queue from two stacks, which deepens your understanding of how queues and stacks interact
- Data Structure: Queues - The fundamentals of queue operations, FIFO ordering, and when to use queues vs. other structures
- Data Structure: Stacks - Stack basics and the LIFO ordering that defines the sandwich stack in this problem
CodeBricks breaks the students unable to eat lunch LeetCode problem into its counting and early termination building blocks, then drills them independently with spaced repetition. You type each piece from scratch until the pattern is automatic. When a simulation-vs-counting question shows up in your interview, you do not think about it. You just write it.